From 4860c73804c6e7ef8e69f98958489bb2bea6f6d2 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 1 Nov 2011 22:23:55 -0200 Subject: staging: Move media drivers to staging/media In practice, it is being hard to distinguish when a patch should go to staging tree or to the media tree. Better to distinguish it, by putting the media drivers at a separate staging directory. Newer staging drivers that include anything with "dvb*.h", "v4l2*.h" or "videodev2.h" should go to the drivers/staging/media tree. Acked-by: Greg Kroah-Hartman Signed-off-by: Mauro Carvalho Chehab --- drivers/staging/Kconfig | 14 +- drivers/staging/Makefile | 8 +- drivers/staging/cxd2099/Kconfig | 12 - drivers/staging/cxd2099/Makefile | 5 - drivers/staging/cxd2099/TODO | 12 - drivers/staging/cxd2099/cxd2099.c | 716 ---- drivers/staging/cxd2099/cxd2099.h | 51 - drivers/staging/dt3155v4l/Kconfig | 28 - drivers/staging/dt3155v4l/Makefile | 1 - drivers/staging/dt3155v4l/dt3155v4l.c | 993 ----- drivers/staging/dt3155v4l/dt3155v4l.h | 212 -- drivers/staging/easycap/Kconfig | 30 - drivers/staging/easycap/Makefile | 10 - drivers/staging/easycap/README | 141 - drivers/staging/easycap/easycap.h | 594 --- drivers/staging/easycap/easycap_ioctl.c | 2450 ------------- drivers/staging/easycap/easycap_low.c | 1129 ------ drivers/staging/easycap/easycap_main.c | 4253 ---------------------- drivers/staging/easycap/easycap_settings.c | 696 ---- drivers/staging/easycap/easycap_sound.c | 816 ----- drivers/staging/easycap/easycap_testcard.c | 155 - drivers/staging/go7007/Kconfig | 109 - drivers/staging/go7007/Makefile | 30 - drivers/staging/go7007/README | 11 - drivers/staging/go7007/go7007-driver.c | 659 ---- drivers/staging/go7007/go7007-fw.c | 1636 --------- drivers/staging/go7007/go7007-i2c.c | 225 -- drivers/staging/go7007/go7007-priv.h | 290 -- drivers/staging/go7007/go7007-usb.c | 1288 ------- drivers/staging/go7007/go7007-v4l2.c | 1839 ---------- drivers/staging/go7007/go7007.h | 114 - drivers/staging/go7007/go7007.txt | 481 --- drivers/staging/go7007/s2250-board.c | 698 ---- drivers/staging/go7007/s2250-loader.c | 191 - drivers/staging/go7007/s2250-loader.h | 24 - drivers/staging/go7007/saa7134-go7007.c | 532 --- drivers/staging/go7007/snd-go7007.c | 306 -- drivers/staging/go7007/wis-i2c.h | 47 - drivers/staging/go7007/wis-ov7640.c | 108 - drivers/staging/go7007/wis-saa7113.c | 336 -- drivers/staging/go7007/wis-saa7115.c | 469 --- drivers/staging/go7007/wis-sony-tuner.c | 720 ---- drivers/staging/go7007/wis-tw2804.c | 357 -- drivers/staging/go7007/wis-tw9903.c | 341 -- drivers/staging/go7007/wis-uda1342.c | 114 - drivers/staging/lirc/Kconfig | 78 - drivers/staging/lirc/Makefile | 14 - drivers/staging/lirc/TODO | 8 - drivers/staging/lirc/TODO.lirc_zilog | 36 - drivers/staging/lirc/lirc_bt829.c | 383 -- drivers/staging/lirc/lirc_ene0100.h | 169 - drivers/staging/lirc/lirc_igorplugusb.c | 577 --- drivers/staging/lirc/lirc_imon.c | 1050 ------ drivers/staging/lirc/lirc_parallel.c | 755 ---- drivers/staging/lirc/lirc_parallel.h | 26 - drivers/staging/lirc/lirc_sasem.c | 939 ----- drivers/staging/lirc/lirc_serial.c | 1315 ------- drivers/staging/lirc/lirc_sir.c | 1279 ------- drivers/staging/lirc/lirc_ttusbir.c | 395 -- drivers/staging/lirc/lirc_zilog.c | 1676 --------- drivers/staging/media/Kconfig | 37 + drivers/staging/media/Makefile | 7 + drivers/staging/media/cxd2099/Kconfig | 12 + drivers/staging/media/cxd2099/Makefile | 5 + drivers/staging/media/cxd2099/TODO | 12 + drivers/staging/media/cxd2099/cxd2099.c | 716 ++++ drivers/staging/media/cxd2099/cxd2099.h | 51 + drivers/staging/media/dt3155v4l/Kconfig | 28 + drivers/staging/media/dt3155v4l/Makefile | 1 + drivers/staging/media/dt3155v4l/dt3155v4l.c | 993 +++++ drivers/staging/media/dt3155v4l/dt3155v4l.h | 212 ++ drivers/staging/media/easycap/Kconfig | 30 + drivers/staging/media/easycap/Makefile | 10 + drivers/staging/media/easycap/README | 141 + drivers/staging/media/easycap/easycap.h | 594 +++ drivers/staging/media/easycap/easycap_ioctl.c | 2450 +++++++++++++ drivers/staging/media/easycap/easycap_low.c | 1129 ++++++ drivers/staging/media/easycap/easycap_main.c | 4253 ++++++++++++++++++++++ drivers/staging/media/easycap/easycap_settings.c | 696 ++++ drivers/staging/media/easycap/easycap_sound.c | 816 +++++ drivers/staging/media/easycap/easycap_testcard.c | 155 + drivers/staging/media/go7007/Kconfig | 109 + drivers/staging/media/go7007/Makefile | 30 + drivers/staging/media/go7007/README | 11 + drivers/staging/media/go7007/go7007-driver.c | 659 ++++ drivers/staging/media/go7007/go7007-fw.c | 1636 +++++++++ drivers/staging/media/go7007/go7007-i2c.c | 225 ++ drivers/staging/media/go7007/go7007-priv.h | 290 ++ drivers/staging/media/go7007/go7007-usb.c | 1288 +++++++ drivers/staging/media/go7007/go7007-v4l2.c | 1839 ++++++++++ drivers/staging/media/go7007/go7007.h | 114 + drivers/staging/media/go7007/go7007.txt | 481 +++ drivers/staging/media/go7007/s2250-board.c | 698 ++++ drivers/staging/media/go7007/s2250-loader.c | 191 + drivers/staging/media/go7007/s2250-loader.h | 24 + drivers/staging/media/go7007/saa7134-go7007.c | 532 +++ drivers/staging/media/go7007/snd-go7007.c | 306 ++ drivers/staging/media/go7007/wis-i2c.h | 47 + drivers/staging/media/go7007/wis-ov7640.c | 108 + drivers/staging/media/go7007/wis-saa7113.c | 336 ++ drivers/staging/media/go7007/wis-saa7115.c | 469 +++ drivers/staging/media/go7007/wis-sony-tuner.c | 720 ++++ drivers/staging/media/go7007/wis-tw2804.c | 357 ++ drivers/staging/media/go7007/wis-tw9903.c | 341 ++ drivers/staging/media/go7007/wis-uda1342.c | 114 + drivers/staging/media/lirc/Kconfig | 78 + drivers/staging/media/lirc/Makefile | 14 + drivers/staging/media/lirc/TODO | 8 + drivers/staging/media/lirc/TODO.lirc_zilog | 36 + drivers/staging/media/lirc/lirc_bt829.c | 383 ++ drivers/staging/media/lirc/lirc_ene0100.h | 169 + drivers/staging/media/lirc/lirc_igorplugusb.c | 577 +++ drivers/staging/media/lirc/lirc_imon.c | 1050 ++++++ drivers/staging/media/lirc/lirc_parallel.c | 755 ++++ drivers/staging/media/lirc/lirc_parallel.h | 26 + drivers/staging/media/lirc/lirc_sasem.c | 939 +++++ drivers/staging/media/lirc/lirc_serial.c | 1315 +++++++ drivers/staging/media/lirc/lirc_sir.c | 1279 +++++++ drivers/staging/media/lirc/lirc_ttusbir.c | 395 ++ drivers/staging/media/lirc/lirc_zilog.c | 1676 +++++++++ drivers/staging/media/solo6x10/Kconfig | 8 + drivers/staging/media/solo6x10/Makefile | 3 + drivers/staging/media/solo6x10/TODO | 24 + drivers/staging/media/solo6x10/core.c | 332 ++ drivers/staging/media/solo6x10/disp.c | 270 ++ drivers/staging/media/solo6x10/enc.c | 238 ++ drivers/staging/media/solo6x10/g723.c | 399 ++ drivers/staging/media/solo6x10/gpio.c | 102 + drivers/staging/media/solo6x10/i2c.c | 330 ++ drivers/staging/media/solo6x10/jpeg.h | 105 + drivers/staging/media/solo6x10/offsets.h | 74 + drivers/staging/media/solo6x10/osd-font.h | 154 + drivers/staging/media/solo6x10/p2m.c | 306 ++ drivers/staging/media/solo6x10/registers.h | 637 ++++ drivers/staging/media/solo6x10/solo6x10.h | 336 ++ drivers/staging/media/solo6x10/tw28.c | 821 +++++ drivers/staging/media/solo6x10/tw28.h | 63 + drivers/staging/media/solo6x10/v4l2-enc.c | 1825 ++++++++++ drivers/staging/media/solo6x10/v4l2.c | 964 +++++ drivers/staging/solo6x10/Kconfig | 8 - drivers/staging/solo6x10/Makefile | 3 - drivers/staging/solo6x10/TODO | 24 - drivers/staging/solo6x10/core.c | 332 -- drivers/staging/solo6x10/disp.c | 270 -- drivers/staging/solo6x10/enc.c | 238 -- drivers/staging/solo6x10/g723.c | 399 -- drivers/staging/solo6x10/gpio.c | 102 - drivers/staging/solo6x10/i2c.c | 330 -- drivers/staging/solo6x10/jpeg.h | 105 - drivers/staging/solo6x10/offsets.h | 74 - drivers/staging/solo6x10/osd-font.h | 154 - drivers/staging/solo6x10/p2m.c | 306 -- drivers/staging/solo6x10/registers.h | 637 ---- drivers/staging/solo6x10/solo6x10.h | 336 -- drivers/staging/solo6x10/tw28.c | 821 ----- drivers/staging/solo6x10/tw28.h | 63 - drivers/staging/solo6x10/v4l2-enc.c | 1825 ---------- drivers/staging/solo6x10/v4l2.c | 964 ----- 158 files changed, 38966 insertions(+), 38940 deletions(-) delete mode 100644 drivers/staging/cxd2099/Kconfig delete mode 100644 drivers/staging/cxd2099/Makefile delete mode 100644 drivers/staging/cxd2099/TODO delete mode 100644 drivers/staging/cxd2099/cxd2099.c delete mode 100644 drivers/staging/cxd2099/cxd2099.h delete mode 100644 drivers/staging/dt3155v4l/Kconfig delete mode 100644 drivers/staging/dt3155v4l/Makefile delete mode 100644 drivers/staging/dt3155v4l/dt3155v4l.c delete mode 100644 drivers/staging/dt3155v4l/dt3155v4l.h delete mode 100644 drivers/staging/easycap/Kconfig delete mode 100644 drivers/staging/easycap/Makefile delete mode 100644 drivers/staging/easycap/README delete mode 100644 drivers/staging/easycap/easycap.h delete mode 100644 drivers/staging/easycap/easycap_ioctl.c delete mode 100644 drivers/staging/easycap/easycap_low.c delete mode 100644 drivers/staging/easycap/easycap_main.c delete mode 100644 drivers/staging/easycap/easycap_settings.c delete mode 100644 drivers/staging/easycap/easycap_sound.c delete mode 100644 drivers/staging/easycap/easycap_testcard.c delete mode 100644 drivers/staging/go7007/Kconfig delete mode 100644 drivers/staging/go7007/Makefile delete mode 100644 drivers/staging/go7007/README delete mode 100644 drivers/staging/go7007/go7007-driver.c delete mode 100644 drivers/staging/go7007/go7007-fw.c delete mode 100644 drivers/staging/go7007/go7007-i2c.c delete mode 100644 drivers/staging/go7007/go7007-priv.h delete mode 100644 drivers/staging/go7007/go7007-usb.c delete mode 100644 drivers/staging/go7007/go7007-v4l2.c delete mode 100644 drivers/staging/go7007/go7007.h delete mode 100644 drivers/staging/go7007/go7007.txt delete mode 100644 drivers/staging/go7007/s2250-board.c delete mode 100644 drivers/staging/go7007/s2250-loader.c delete mode 100644 drivers/staging/go7007/s2250-loader.h delete mode 100644 drivers/staging/go7007/saa7134-go7007.c delete mode 100644 drivers/staging/go7007/snd-go7007.c delete mode 100644 drivers/staging/go7007/wis-i2c.h delete mode 100644 drivers/staging/go7007/wis-ov7640.c delete mode 100644 drivers/staging/go7007/wis-saa7113.c delete mode 100644 drivers/staging/go7007/wis-saa7115.c delete mode 100644 drivers/staging/go7007/wis-sony-tuner.c delete mode 100644 drivers/staging/go7007/wis-tw2804.c delete mode 100644 drivers/staging/go7007/wis-tw9903.c delete mode 100644 drivers/staging/go7007/wis-uda1342.c delete mode 100644 drivers/staging/lirc/Kconfig delete mode 100644 drivers/staging/lirc/Makefile delete mode 100644 drivers/staging/lirc/TODO delete mode 100644 drivers/staging/lirc/TODO.lirc_zilog delete mode 100644 drivers/staging/lirc/lirc_bt829.c delete mode 100644 drivers/staging/lirc/lirc_ene0100.h delete mode 100644 drivers/staging/lirc/lirc_igorplugusb.c delete mode 100644 drivers/staging/lirc/lirc_imon.c delete mode 100644 drivers/staging/lirc/lirc_parallel.c delete mode 100644 drivers/staging/lirc/lirc_parallel.h delete mode 100644 drivers/staging/lirc/lirc_sasem.c delete mode 100644 drivers/staging/lirc/lirc_serial.c delete mode 100644 drivers/staging/lirc/lirc_sir.c delete mode 100644 drivers/staging/lirc/lirc_ttusbir.c delete mode 100644 drivers/staging/lirc/lirc_zilog.c create mode 100644 drivers/staging/media/Kconfig create mode 100644 drivers/staging/media/Makefile create mode 100644 drivers/staging/media/cxd2099/Kconfig create mode 100644 drivers/staging/media/cxd2099/Makefile create mode 100644 drivers/staging/media/cxd2099/TODO create mode 100644 drivers/staging/media/cxd2099/cxd2099.c create mode 100644 drivers/staging/media/cxd2099/cxd2099.h create mode 100644 drivers/staging/media/dt3155v4l/Kconfig create mode 100644 drivers/staging/media/dt3155v4l/Makefile create mode 100644 drivers/staging/media/dt3155v4l/dt3155v4l.c create mode 100644 drivers/staging/media/dt3155v4l/dt3155v4l.h create mode 100644 drivers/staging/media/easycap/Kconfig create mode 100644 drivers/staging/media/easycap/Makefile create mode 100644 drivers/staging/media/easycap/README create mode 100644 drivers/staging/media/easycap/easycap.h create mode 100644 drivers/staging/media/easycap/easycap_ioctl.c create mode 100644 drivers/staging/media/easycap/easycap_low.c create mode 100644 drivers/staging/media/easycap/easycap_main.c create mode 100644 drivers/staging/media/easycap/easycap_settings.c create mode 100644 drivers/staging/media/easycap/easycap_sound.c create mode 100644 drivers/staging/media/easycap/easycap_testcard.c create mode 100644 drivers/staging/media/go7007/Kconfig create mode 100644 drivers/staging/media/go7007/Makefile create mode 100644 drivers/staging/media/go7007/README create mode 100644 drivers/staging/media/go7007/go7007-driver.c create mode 100644 drivers/staging/media/go7007/go7007-fw.c create mode 100644 drivers/staging/media/go7007/go7007-i2c.c create mode 100644 drivers/staging/media/go7007/go7007-priv.h create mode 100644 drivers/staging/media/go7007/go7007-usb.c create mode 100644 drivers/staging/media/go7007/go7007-v4l2.c create mode 100644 drivers/staging/media/go7007/go7007.h create mode 100644 drivers/staging/media/go7007/go7007.txt create mode 100644 drivers/staging/media/go7007/s2250-board.c create mode 100644 drivers/staging/media/go7007/s2250-loader.c create mode 100644 drivers/staging/media/go7007/s2250-loader.h create mode 100644 drivers/staging/media/go7007/saa7134-go7007.c create mode 100644 drivers/staging/media/go7007/snd-go7007.c create mode 100644 drivers/staging/media/go7007/wis-i2c.h create mode 100644 drivers/staging/media/go7007/wis-ov7640.c create mode 100644 drivers/staging/media/go7007/wis-saa7113.c create mode 100644 drivers/staging/media/go7007/wis-saa7115.c create mode 100644 drivers/staging/media/go7007/wis-sony-tuner.c create mode 100644 drivers/staging/media/go7007/wis-tw2804.c create mode 100644 drivers/staging/media/go7007/wis-tw9903.c create mode 100644 drivers/staging/media/go7007/wis-uda1342.c create mode 100644 drivers/staging/media/lirc/Kconfig create mode 100644 drivers/staging/media/lirc/Makefile create mode 100644 drivers/staging/media/lirc/TODO create mode 100644 drivers/staging/media/lirc/TODO.lirc_zilog create mode 100644 drivers/staging/media/lirc/lirc_bt829.c create mode 100644 drivers/staging/media/lirc/lirc_ene0100.h create mode 100644 drivers/staging/media/lirc/lirc_igorplugusb.c create mode 100644 drivers/staging/media/lirc/lirc_imon.c create mode 100644 drivers/staging/media/lirc/lirc_parallel.c create mode 100644 drivers/staging/media/lirc/lirc_parallel.h create mode 100644 drivers/staging/media/lirc/lirc_sasem.c create mode 100644 drivers/staging/media/lirc/lirc_serial.c create mode 100644 drivers/staging/media/lirc/lirc_sir.c create mode 100644 drivers/staging/media/lirc/lirc_ttusbir.c create mode 100644 drivers/staging/media/lirc/lirc_zilog.c create mode 100644 drivers/staging/media/solo6x10/Kconfig create mode 100644 drivers/staging/media/solo6x10/Makefile create mode 100644 drivers/staging/media/solo6x10/TODO create mode 100644 drivers/staging/media/solo6x10/core.c create mode 100644 drivers/staging/media/solo6x10/disp.c create mode 100644 drivers/staging/media/solo6x10/enc.c create mode 100644 drivers/staging/media/solo6x10/g723.c create mode 100644 drivers/staging/media/solo6x10/gpio.c create mode 100644 drivers/staging/media/solo6x10/i2c.c create mode 100644 drivers/staging/media/solo6x10/jpeg.h create mode 100644 drivers/staging/media/solo6x10/offsets.h create mode 100644 drivers/staging/media/solo6x10/osd-font.h create mode 100644 drivers/staging/media/solo6x10/p2m.c create mode 100644 drivers/staging/media/solo6x10/registers.h create mode 100644 drivers/staging/media/solo6x10/solo6x10.h create mode 100644 drivers/staging/media/solo6x10/tw28.c create mode 100644 drivers/staging/media/solo6x10/tw28.h create mode 100644 drivers/staging/media/solo6x10/v4l2-enc.c create mode 100644 drivers/staging/media/solo6x10/v4l2.c delete mode 100644 drivers/staging/solo6x10/Kconfig delete mode 100644 drivers/staging/solo6x10/Makefile delete mode 100644 drivers/staging/solo6x10/TODO delete mode 100644 drivers/staging/solo6x10/core.c delete mode 100644 drivers/staging/solo6x10/disp.c delete mode 100644 drivers/staging/solo6x10/enc.c delete mode 100644 drivers/staging/solo6x10/g723.c delete mode 100644 drivers/staging/solo6x10/gpio.c delete mode 100644 drivers/staging/solo6x10/i2c.c delete mode 100644 drivers/staging/solo6x10/jpeg.h delete mode 100644 drivers/staging/solo6x10/offsets.h delete mode 100644 drivers/staging/solo6x10/osd-font.h delete mode 100644 drivers/staging/solo6x10/p2m.c delete mode 100644 drivers/staging/solo6x10/registers.h delete mode 100644 drivers/staging/solo6x10/solo6x10.h delete mode 100644 drivers/staging/solo6x10/tw28.c delete mode 100644 drivers/staging/solo6x10/tw28.h delete mode 100644 drivers/staging/solo6x10/v4l2-enc.c delete mode 100644 drivers/staging/solo6x10/v4l2.c (limited to 'drivers/staging') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 39df8597d310..25cdff36a78a 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -30,10 +30,6 @@ source "drivers/staging/et131x/Kconfig" source "drivers/staging/slicoss/Kconfig" -source "drivers/staging/go7007/Kconfig" - -source "drivers/staging/cxd2099/Kconfig" - source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" @@ -102,20 +98,12 @@ source "drivers/staging/wlags49_h25/Kconfig" source "drivers/staging/sm7xx/Kconfig" -source "drivers/staging/dt3155v4l/Kconfig" - source "drivers/staging/crystalhd/Kconfig" source "drivers/staging/cxt1e1/Kconfig" source "drivers/staging/xgifb/Kconfig" -source "drivers/staging/lirc/Kconfig" - -source "drivers/staging/easycap/Kconfig" - -source "drivers/staging/solo6x10/Kconfig" - source "drivers/staging/tidspbridge/Kconfig" source "drivers/staging/quickstart/Kconfig" @@ -142,6 +130,6 @@ source "drivers/staging/mei/Kconfig" source "drivers/staging/nvec/Kconfig" -source "drivers/staging/media/as102/Kconfig" +source "drivers/staging/media/Kconfig" endif # STAGING diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index cd1bcc1f0e66..a25f3f26c7ff 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -4,11 +4,9 @@ obj-$(CONFIG_STAGING) += staging.o obj-y += serial/ +obj-y += media/ obj-$(CONFIG_ET131X) += et131x/ obj-$(CONFIG_SLICOSS) += slicoss/ -obj-$(CONFIG_VIDEO_GO7007) += go7007/ -obj-$(CONFIG_DVB_CXD2099) += cxd2099/ -obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USBIP_CORE) += usbip/ obj-$(CONFIG_W35UND) += winbond/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ @@ -43,12 +41,9 @@ obj-$(CONFIG_ZCACHE) += zcache/ obj-$(CONFIG_WLAGS49_H2) += wlags49_h2/ obj-$(CONFIG_WLAGS49_H25) += wlags49_h25/ obj-$(CONFIG_FB_SM7XX) += sm7xx/ -obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ obj-$(CONFIG_CRYSTALHD) += crystalhd/ obj-$(CONFIG_CXT1E1) += cxt1e1/ obj-$(CONFIG_FB_XGI) += xgifb/ -obj-$(CONFIG_EASYCAP) += easycap/ -obj-$(CONFIG_SOLO6X10) += solo6x10/ obj-$(CONFIG_TIDSPBRIDGE) += tidspbridge/ obj-$(CONFIG_ACPI_QUICKSTART) += quickstart/ obj-$(CONFIG_SBE_2T3E3) += sbe-2t3e3/ @@ -62,4 +57,3 @@ obj-$(CONFIG_TOUCHSCREEN_SYNAPTICS_I2C_RMI4) += ste_rmi4/ obj-$(CONFIG_DRM_PSB) += gma500/ obj-$(CONFIG_INTEL_MEI) += mei/ obj-$(CONFIG_MFD_NVEC) += nvec/ -obj-$(CONFIG_DVB_AS102) += media/as102/ diff --git a/drivers/staging/cxd2099/Kconfig b/drivers/staging/cxd2099/Kconfig deleted file mode 100644 index b48aefddc84c..000000000000 --- a/drivers/staging/cxd2099/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config DVB_CXD2099 - tristate "CXD2099AR Common Interface driver" - depends on DVB_CORE && PCI && I2C - ---help--- - Support for the CI module found on cards based on - - Micronas ngene PCIe bridge: cineS2 etc. - - Digital Devices PCIe bridge: Octopus series - - For now, data is passed through '/dev/dvb/adapterX/sec0': - - Encrypted data must be written to 'sec0'. - - Decrypted data can be read from 'sec0'. - - Setup the CAM using device 'ca0'. diff --git a/drivers/staging/cxd2099/Makefile b/drivers/staging/cxd2099/Makefile deleted file mode 100644 index 64cfc77be357..000000000000 --- a/drivers/staging/cxd2099/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -obj-$(CONFIG_DVB_CXD2099) += cxd2099.o - -ccflags-y += -Idrivers/media/dvb/dvb-core/ -ccflags-y += -Idrivers/media/dvb/frontends/ -ccflags-y += -Idrivers/media/common/tuners/ diff --git a/drivers/staging/cxd2099/TODO b/drivers/staging/cxd2099/TODO deleted file mode 100644 index 375bb6f8ee2c..000000000000 --- a/drivers/staging/cxd2099/TODO +++ /dev/null @@ -1,12 +0,0 @@ -For now, data is passed through '/dev/dvb/adapterX/sec0': - - Encrypted data must be written to 'sec0'. - - Decrypted data can be read from 'sec0'. - - Setup the CAM using device 'ca0'. - -But this is wrong. There are some discussions about the proper way for -doing it, as seen at: - http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html - -While there's no proper fix for it, the driver should be kept in staging. - -Patches should be submitted to: linux-media@vger.kernel.org. diff --git a/drivers/staging/cxd2099/cxd2099.c b/drivers/staging/cxd2099/cxd2099.c deleted file mode 100644 index 1c04185bcfd7..000000000000 --- a/drivers/staging/cxd2099/cxd2099.c +++ /dev/null @@ -1,716 +0,0 @@ -/* - * cxd2099.c: Driver for the CXD2099AR Common Interface Controller - * - * 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 "cxd2099.h" - -#define MAX_BUFFER_SIZE 248 - -struct cxd { - struct dvb_ca_en50221 en; - - struct i2c_adapter *i2c; - struct cxd2099_cfg cfg; - - u8 regs[0x23]; - u8 lastaddress; - u8 clk_reg_f; - u8 clk_reg_b; - int mode; - int ready; - int dr; - int slot_stat; - - u8 amem[1024]; - int amem_read; - - int cammode; - struct mutex lock; -}; - -static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, - u8 reg, u8 data) -{ - u8 m[2] = {reg, data}; - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", - reg, adr); - return -1; - } - return 0; -} - -static int i2c_write(struct i2c_adapter *adapter, u8 adr, - u8 *data, u8 len) -{ - struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; - - if (i2c_transfer(adapter, &msg, 1) != 1) { - printk(KERN_ERR "Failed to write to I2C!\n"); - return -1; - } - return 0; -} - -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} }; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read_reg\n"); - return -1; - } - return 0; -} - -static int i2c_read(struct i2c_adapter *adapter, u8 adr, - u8 reg, u8 *data, u8 n) -{ - struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, - .buf = ®, .len = 1}, - {.addr = adr, .flags = I2C_M_RD, - .buf = data, .len = n} }; - - if (i2c_transfer(adapter, msgs, 2) != 2) { - printk(KERN_ERR "error in i2c_read\n"); - return -1; - } - return 0; -} - -static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n) -{ - int status; - - status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); - if (!status) { - ci->lastaddress = adr; - status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n); - } - return status; -} - -static int read_reg(struct cxd *ci, u8 reg, u8 *val) -{ - return read_block(ci, reg, val, 1); -} - - -static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) -{ - int status; - u8 addr[3] = {2, address & 0xff, address >> 8}; - - status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); - if (!status) - status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n); - return status; -} - -static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) -{ - int status; - u8 addr[3] = {2, address & 0xff, address >> 8}; - - status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); - if (!status) { - u8 buf[256] = {3}; - memcpy(buf+1, data, n); - status = i2c_write(ci->i2c, ci->cfg.adr, buf, n+1); - } - return status; -} - -static int read_io(struct cxd *ci, u16 address, u8 *val) -{ - int status; - u8 addr[3] = {2, address & 0xff, address >> 8}; - - status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); - if (!status) - status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1); - return status; -} - -static int write_io(struct cxd *ci, u16 address, u8 val) -{ - int status; - u8 addr[3] = {2, address & 0xff, address >> 8}; - u8 buf[2] = {3, val}; - - status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); - if (!status) - status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2); - return status; -} - -#if 0 -static int read_io_data(struct cxd *ci, u8 *data, u8 n) -{ - int status; - u8 addr[3] = { 2, 0, 0 }; - - status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); - if (!status) - status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n); - return 0; -} - -static int write_io_data(struct cxd *ci, u8 *data, u8 n) -{ - int status; - u8 addr[3] = {2, 0, 0}; - - status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); - if (!status) { - u8 buf[256] = {3}; - memcpy(buf+1, data, n); - status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1); - } - return 0; -} -#endif - -static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) -{ - int status; - - status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg); - if (!status && reg >= 6 && reg <= 8 && mask != 0xff) - status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]); - ci->regs[reg] = (ci->regs[reg] & (~mask)) | val; - if (!status) { - ci->lastaddress = reg; - status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]); - } - if (reg == 0x20) - ci->regs[reg] &= 0x7f; - return status; -} - -static int write_reg(struct cxd *ci, u8 reg, u8 val) -{ - return write_regm(ci, reg, val, 0xff); -} - -#ifdef BUFFER_MODE -static int write_block(struct cxd *ci, u8 adr, u8 *data, int n) -{ - int status; - u8 buf[256] = {1}; - - status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); - if (!status) { - ci->lastaddress = adr; - memcpy(buf + 1, data, n); - status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1); - } - return status; -} -#endif - -static void set_mode(struct cxd *ci, int mode) -{ - if (mode == ci->mode) - return; - - switch (mode) { - case 0x00: /* IO mem */ - write_regm(ci, 0x06, 0x00, 0x07); - break; - case 0x01: /* ATT mem */ - write_regm(ci, 0x06, 0x02, 0x07); - break; - default: - break; - } - ci->mode = mode; -} - -static void cam_mode(struct cxd *ci, int mode) -{ - if (mode == ci->cammode) - return; - - switch (mode) { - case 0x00: - write_regm(ci, 0x20, 0x80, 0x80); - break; - case 0x01: -#ifdef BUFFER_MODE - if (!ci->en.read_data) - return; - printk(KERN_INFO "enable cam buffer mode\n"); - /* write_reg(ci, 0x0d, 0x00); */ - /* write_reg(ci, 0x0e, 0x01); */ - write_regm(ci, 0x08, 0x40, 0x40); - /* read_reg(ci, 0x12, &dummy); */ - write_regm(ci, 0x08, 0x80, 0x80); -#endif - break; - default: - break; - } - ci->cammode = mode; -} - - - -static int init(struct cxd *ci) -{ - int status; - - mutex_lock(&ci->lock); - ci->mode = -1; - do { - status = write_reg(ci, 0x00, 0x00); - if (status < 0) - break; - status = write_reg(ci, 0x01, 0x00); - if (status < 0) - break; - status = write_reg(ci, 0x02, 0x10); - if (status < 0) - break; - status = write_reg(ci, 0x03, 0x00); - if (status < 0) - break; - status = write_reg(ci, 0x05, 0xFF); - if (status < 0) - break; - status = write_reg(ci, 0x06, 0x1F); - if (status < 0) - break; - status = write_reg(ci, 0x07, 0x1F); - if (status < 0) - break; - status = write_reg(ci, 0x08, 0x28); - if (status < 0) - break; - status = write_reg(ci, 0x14, 0x20); - if (status < 0) - break; - -#if 0 - status = write_reg(ci, 0x09, 0x4D); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */ - if (status < 0) - break; -#endif - status = write_reg(ci, 0x0A, 0xA7); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */ - if (status < 0) - break; - - status = write_reg(ci, 0x0B, 0x33); - if (status < 0) - break; - status = write_reg(ci, 0x0C, 0x33); - if (status < 0) - break; - - status = write_regm(ci, 0x14, 0x00, 0x0F); - if (status < 0) - break; - status = write_reg(ci, 0x15, ci->clk_reg_b); - if (status < 0) - break; - status = write_regm(ci, 0x16, 0x00, 0x0F); - if (status < 0) - break; - status = write_reg(ci, 0x17, ci->clk_reg_f); - if (status < 0) - break; - - if (ci->cfg.clock_mode) { - if (ci->cfg.polarity) { - status = write_reg(ci, 0x09, 0x6f); - if (status < 0) - break; - } else { - status = write_reg(ci, 0x09, 0x6d); - if (status < 0) - break; - } - status = write_reg(ci, 0x20, 0x68); - if (status < 0) - break; - status = write_reg(ci, 0x21, 0x00); - if (status < 0) - break; - status = write_reg(ci, 0x22, 0x02); - if (status < 0) - break; - } else { - if (ci->cfg.polarity) { - status = write_reg(ci, 0x09, 0x4f); - if (status < 0) - break; - } else { - status = write_reg(ci, 0x09, 0x4d); - if (status < 0) - break; - } - - status = write_reg(ci, 0x20, 0x28); - if (status < 0) - break; - status = write_reg(ci, 0x21, 0x00); - if (status < 0) - break; - status = write_reg(ci, 0x22, 0x07); - if (status < 0) - break; - } - - status = write_regm(ci, 0x20, 0x80, 0x80); - if (status < 0) - break; - status = write_regm(ci, 0x03, 0x02, 0x02); - if (status < 0) - break; - status = write_reg(ci, 0x01, 0x04); - if (status < 0) - break; - status = write_reg(ci, 0x00, 0x31); - if (status < 0) - break; - - /* Put TS in bypass */ - status = write_regm(ci, 0x09, 0x08, 0x08); - if (status < 0) - break; - ci->cammode = -1; - cam_mode(ci, 0); - } while (0); - mutex_unlock(&ci->lock); - - return 0; -} - -static int read_attribute_mem(struct dvb_ca_en50221 *ca, - int slot, int address) -{ - struct cxd *ci = ca->data; -#if 0 - if (ci->amem_read) { - if (address <= 0 || address > 1024) - return -EIO; - return ci->amem[address]; - } - - mutex_lock(&ci->lock); - write_regm(ci, 0x06, 0x00, 0x05); - read_pccard(ci, 0, &ci->amem[0], 128); - read_pccard(ci, 128, &ci->amem[0], 128); - read_pccard(ci, 256, &ci->amem[0], 128); - read_pccard(ci, 384, &ci->amem[0], 128); - write_regm(ci, 0x06, 0x05, 0x05); - mutex_unlock(&ci->lock); - return ci->amem[address]; -#else - u8 val; - mutex_lock(&ci->lock); - set_mode(ci, 1); - read_pccard(ci, address, &val, 1); - mutex_unlock(&ci->lock); - /* printk(KERN_INFO "%02x:%02x\n", address,val); */ - return val; -#endif -} - -static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, - int address, u8 value) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - set_mode(ci, 1); - write_pccard(ci, address, &value, 1); - mutex_unlock(&ci->lock); - return 0; -} - -static int read_cam_control(struct dvb_ca_en50221 *ca, - int slot, u8 address) -{ - struct cxd *ci = ca->data; - u8 val; - - mutex_lock(&ci->lock); - set_mode(ci, 0); - read_io(ci, address, &val); - mutex_unlock(&ci->lock); - return val; -} - -static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, - u8 address, u8 value) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - set_mode(ci, 0); - write_io(ci, address, value); - mutex_unlock(&ci->lock); - return 0; -} - -static int slot_reset(struct dvb_ca_en50221 *ca, int slot) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); -#if 0 - write_reg(ci, 0x00, 0x21); - write_reg(ci, 0x06, 0x1F); - write_reg(ci, 0x00, 0x31); -#else -#if 0 - write_reg(ci, 0x06, 0x1F); - write_reg(ci, 0x06, 0x2F); -#else - cam_mode(ci, 0); - write_reg(ci, 0x00, 0x21); - write_reg(ci, 0x06, 0x1F); - write_reg(ci, 0x00, 0x31); - write_regm(ci, 0x20, 0x80, 0x80); - write_reg(ci, 0x03, 0x02); - ci->ready = 0; -#endif -#endif - ci->mode = -1; - { - int i; -#if 0 - u8 val; -#endif - for (i = 0; i < 100; i++) { - msleep(10); -#if 0 - read_reg(ci, 0x06, &val); - printk(KERN_INFO "%d:%02x\n", i, val); - if (!(val&0x10)) - break; -#else - if (ci->ready) - break; -#endif - } - } - mutex_unlock(&ci->lock); - /* msleep(500); */ - return 0; -} - -static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) -{ - struct cxd *ci = ca->data; - - printk(KERN_INFO "slot_shutdown\n"); - mutex_lock(&ci->lock); - write_regm(ci, 0x09, 0x08, 0x08); - write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ - write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */ - ci->mode = -1; - mutex_unlock(&ci->lock); - return 0; -} - -static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - write_regm(ci, 0x09, 0x00, 0x08); - set_mode(ci, 0); -#ifdef BUFFER_MODE - cam_mode(ci, 1); -#endif - mutex_unlock(&ci->lock); - return 0; -} - - -static int campoll(struct cxd *ci) -{ - u8 istat; - - read_reg(ci, 0x04, &istat); - if (!istat) - return 0; - write_reg(ci, 0x05, istat); - - if (istat&0x40) { - ci->dr = 1; - printk(KERN_INFO "DR\n"); - } - if (istat&0x20) - printk(KERN_INFO "WC\n"); - - if (istat&2) { - u8 slotstat; - - read_reg(ci, 0x01, &slotstat); - if (!(2&slotstat)) { - if (!ci->slot_stat) { - ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; - write_regm(ci, 0x03, 0x08, 0x08); - } - - } else { - if (ci->slot_stat) { - ci->slot_stat = 0; - write_regm(ci, 0x03, 0x00, 0x08); - printk(KERN_INFO "NO CAM\n"); - ci->ready = 0; - } - } - if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { - ci->ready = 1; - ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; - } - } - return 0; -} - - -static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) -{ - struct cxd *ci = ca->data; - u8 slotstat; - - mutex_lock(&ci->lock); - campoll(ci); - read_reg(ci, 0x01, &slotstat); - mutex_unlock(&ci->lock); - - return ci->slot_stat; -} - -#ifdef BUFFER_MODE -static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) -{ - struct cxd *ci = ca->data; - u8 msb, lsb; - u16 len; - - mutex_lock(&ci->lock); - campoll(ci); - mutex_unlock(&ci->lock); - - printk(KERN_INFO "read_data\n"); - if (!ci->dr) - return 0; - - mutex_lock(&ci->lock); - read_reg(ci, 0x0f, &msb); - read_reg(ci, 0x10, &lsb); - len = (msb<<8)|lsb; - read_block(ci, 0x12, ebuf, len); - ci->dr = 0; - mutex_unlock(&ci->lock); - - return len; -} - -static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) -{ - struct cxd *ci = ca->data; - - mutex_lock(&ci->lock); - printk(kern_INFO "write_data %d\n", ecount); - write_reg(ci, 0x0d, ecount>>8); - write_reg(ci, 0x0e, ecount&0xff); - write_block(ci, 0x11, ebuf, ecount); - mutex_unlock(&ci->lock); - return ecount; -} -#endif - -static struct dvb_ca_en50221 en_templ = { - .read_attribute_mem = read_attribute_mem, - .write_attribute_mem = write_attribute_mem, - .read_cam_control = read_cam_control, - .write_cam_control = write_cam_control, - .slot_reset = slot_reset, - .slot_shutdown = slot_shutdown, - .slot_ts_enable = slot_ts_enable, - .poll_slot_status = poll_slot_status, -#ifdef BUFFER_MODE - .read_data = read_data, - .write_data = write_data, -#endif - -}; - -struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, - void *priv, - struct i2c_adapter *i2c) -{ - struct cxd *ci = 0; - u8 val; - - if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) { - printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr); - return 0; - } - - ci = kmalloc(sizeof(struct cxd), GFP_KERNEL); - if (!ci) - return 0; - memset(ci, 0, sizeof(*ci)); - - mutex_init(&ci->lock); - memcpy(&ci->cfg, cfg, sizeof(struct cxd2099_cfg)); - ci->i2c = i2c; - ci->lastaddress = 0xff; - ci->clk_reg_b = 0x4a; - ci->clk_reg_f = 0x1b; - - memcpy(&ci->en, &en_templ, sizeof(en_templ)); - ci->en.data = ci; - init(ci); - printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr); - return &ci->en; -} -EXPORT_SYMBOL(cxd2099_attach); - -MODULE_DESCRIPTION("cxd2099"); -MODULE_AUTHOR("Ralph Metzler"); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/cxd2099/cxd2099.h b/drivers/staging/cxd2099/cxd2099.h deleted file mode 100644 index 19c588a59588..000000000000 --- a/drivers/staging/cxd2099/cxd2099.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * cxd2099.h: Driver for the CXD2099AR Common Interface Controller - * - * 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 _CXD2099_H_ -#define _CXD2099_H_ - -#include - -struct cxd2099_cfg { - u32 bitrate; - u8 adr; - u8 polarity:1; - u8 clock_mode:1; -}; - -#if defined(CONFIG_DVB_CXD2099) || \ - (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE)) -struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, - void *priv, struct i2c_adapter *i2c); -#else - -static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, - void *priv, struct i2c_adapter *i2c) -{ - printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); - return NULL; -} -#endif - -#endif diff --git a/drivers/staging/dt3155v4l/Kconfig b/drivers/staging/dt3155v4l/Kconfig deleted file mode 100644 index 226a1ca90b3c..000000000000 --- a/drivers/staging/dt3155v4l/Kconfig +++ /dev/null @@ -1,28 +0,0 @@ -config VIDEO_DT3155 - tristate "DT3155 frame grabber, Video4Linux interface" - depends on PCI && VIDEO_DEV && VIDEO_V4L2 - select VIDEOBUF2_DMA_CONTIG - default n - ---help--- - Enables dt3155 device driver for the DataTranslation DT3155 frame grabber. - Say Y here if you have this hardware. - In doubt, say N. - - To compile this driver as a module, choose M here: the - module will be called dt3155v4l. - -config DT3155_CCIR - bool "Selects CCIR/50Hz vertical refresh" - depends on VIDEO_DT3155 - default y - ---help--- - Select it for CCIR/50Hz (European region), - or leave it unselected for RS-170/60Hz (North America). - -config DT3155_STREAMING - bool "Selects streaming capture method" - depends on VIDEO_DT3155 - default y - ---help--- - Select it if you want to use streaming of memory mapped buffers - or leave it unselected if you want to use read method (one copy more). diff --git a/drivers/staging/dt3155v4l/Makefile b/drivers/staging/dt3155v4l/Makefile deleted file mode 100644 index ce7a3ec2faf3..000000000000 --- a/drivers/staging/dt3155v4l/Makefile +++ /dev/null @@ -1 +0,0 @@ -obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l.o diff --git a/drivers/staging/dt3155v4l/dt3155v4l.c b/drivers/staging/dt3155v4l/dt3155v4l.c deleted file mode 100644 index 04e93c49f03a..000000000000 --- a/drivers/staging/dt3155v4l/dt3155v4l.c +++ /dev/null @@ -1,993 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Marin Mitov * - * mitov@issp.bas.bg * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public 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 "dt3155v4l.h" - -#define DT3155_VENDOR_ID 0x8086 -#define DT3155_DEVICE_ID 0x1223 - -/* DT3155_CHUNK_SIZE is 4M (2^22) 8 full size buffers */ -#define DT3155_CHUNK_SIZE (1U << 22) - -#define DT3155_COH_FLAGS (GFP_KERNEL | GFP_DMA32 | __GFP_COLD | __GFP_NOWARN) - -#define DT3155_BUF_SIZE (768 * 576) - -#ifdef CONFIG_DT3155_STREAMING -#define DT3155_CAPTURE_METHOD V4L2_CAP_STREAMING -#else -#define DT3155_CAPTURE_METHOD V4L2_CAP_READWRITE -#endif - -/* global initializers (for all boards) */ -#ifdef CONFIG_DT3155_CCIR -static const u8 csr2_init = VT_50HZ; -#define DT3155_CURRENT_NORM V4L2_STD_625_50 -static const unsigned int img_width = 768; -static const unsigned int img_height = 576; -static const unsigned int frames_per_sec = 25; -static const struct v4l2_fmtdesc frame_std[] = { - { - .index = 0, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .flags = 0, - .description = "CCIR/50Hz 8 bits gray", - .pixelformat = V4L2_PIX_FMT_GREY, - }, -}; -#else -static const u8 csr2_init = VT_60HZ; -#define DT3155_CURRENT_NORM V4L2_STD_525_60 -static const unsigned int img_width = 640; -static const unsigned int img_height = 480; -static const unsigned int frames_per_sec = 30; -static const struct v4l2_fmtdesc frame_std[] = { - { - .index = 0, - .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, - .flags = 0, - .description = "RS-170/60Hz 8 bits gray", - .pixelformat = V4L2_PIX_FMT_GREY, - }, -}; -#endif - -#define NUM_OF_FORMATS ARRAY_SIZE(frame_std) - -static u8 config_init = ACQ_MODE_EVEN; - -/** - * read_i2c_reg - reads an internal i2c register - * - * @addr: dt3155 mmio base address - * @index: index (internal address) of register to read - * @data: pointer to byte the read data will be placed in - * - * returns: zero on success or error code - * - * This function starts reading the specified (by index) register - * and busy waits for the process to finish. The result is placed - * in a byte pointed by data. - */ -static int -read_i2c_reg(void __iomem *addr, u8 index, u8 *data) -{ - u32 tmp = index; - - iowrite32((tmp<<17) | IIC_READ, addr + IIC_CSR2); - mmiowb(); - udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */ - if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) - return -EIO; /* error: NEW_CYCLE not cleared */ - tmp = ioread32(addr + IIC_CSR1); - if (tmp & DIRECT_ABORT) { - /* reset DIRECT_ABORT bit */ - iowrite32(DIRECT_ABORT, addr + IIC_CSR1); - return -EIO; /* error: DIRECT_ABORT set */ - } - *data = tmp>>24; - return 0; -} - -/** - * write_i2c_reg - writes to an internal i2c register - * - * @addr: dt3155 mmio base address - * @index: index (internal address) of register to read - * @data: data to be written - * - * returns: zero on success or error code - * - * This function starts writting the specified (by index) register - * and busy waits for the process to finish. - */ -static int -write_i2c_reg(void __iomem *addr, u8 index, u8 data) -{ - u32 tmp = index; - - iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2); - mmiowb(); - udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ - if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) - return -EIO; /* error: NEW_CYCLE not cleared */ - if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { - /* reset DIRECT_ABORT bit */ - iowrite32(DIRECT_ABORT, addr + IIC_CSR1); - return -EIO; /* error: DIRECT_ABORT set */ - } - return 0; -} - -/** - * write_i2c_reg_nowait - writes to an internal i2c register - * - * @addr: dt3155 mmio base address - * @index: index (internal address) of register to read - * @data: data to be written - * - * This function starts writting the specified (by index) register - * and then returns. - */ -static void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data) -{ - u32 tmp = index; - - iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2); - mmiowb(); -} - -/** - * wait_i2c_reg - waits the read/write to finish - * - * @addr: dt3155 mmio base address - * - * returns: zero on success or error code - * - * This function waits reading/writting to finish. - */ -static int wait_i2c_reg(void __iomem *addr) -{ - if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) - udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ - if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) - return -EIO; /* error: NEW_CYCLE not cleared */ - if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { - /* reset DIRECT_ABORT bit */ - iowrite32(DIRECT_ABORT, addr + IIC_CSR1); - return -EIO; /* error: DIRECT_ABORT set */ - } - return 0; -} - -static int -dt3155_start_acq(struct dt3155_priv *pd) -{ - struct vb2_buffer *vb = pd->curr_buf; - dma_addr_t dma_addr; - - dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); - iowrite32(dma_addr, pd->regs + EVEN_DMA_START); - iowrite32(dma_addr + img_width, pd->regs + ODD_DMA_START); - iowrite32(img_width, pd->regs + EVEN_DMA_STRIDE); - iowrite32(img_width, pd->regs + ODD_DMA_STRIDE); - /* enable interrupts, clear all irq flags */ - iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | - FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR); - iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | - FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD, - pd->regs + CSR1); - wait_i2c_reg(pd->regs); - write_i2c_reg(pd->regs, CONFIG, pd->config); - write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); - write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); - - /* start the board */ - write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD); - return 0; /* success */ -} - -/* - * driver-specific callbacks (vb2_ops) - */ -static int -dt3155_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, - unsigned int *num_planes, unsigned long sizes[], - void *alloc_ctxs[]) -{ - struct dt3155_priv *pd = vb2_get_drv_priv(q); - void *ret; - - if (*num_buffers == 0) - *num_buffers = 1; - *num_planes = 1; - sizes[0] = img_width * img_height; - if (pd->q->alloc_ctx[0]) - return 0; - ret = vb2_dma_contig_init_ctx(&pd->pdev->dev); - if (IS_ERR(ret)) - return PTR_ERR(ret); - pd->q->alloc_ctx[0] = ret; - return 0; -} - -static void -dt3155_wait_prepare(struct vb2_queue *q) -{ - struct dt3155_priv *pd = vb2_get_drv_priv(q); - - mutex_unlock(pd->vdev->lock); -} - -static void -dt3155_wait_finish(struct vb2_queue *q) -{ - struct dt3155_priv *pd = vb2_get_drv_priv(q); - - mutex_lock(pd->vdev->lock); -} - -static int -dt3155_buf_prepare(struct vb2_buffer *vb) -{ - vb2_set_plane_payload(vb, 0, img_width * img_height); - return 0; -} - -static int -dt3155_start_streaming(struct vb2_queue *q) -{ - return 0; -} - -static int -dt3155_stop_streaming(struct vb2_queue *q) -{ - struct dt3155_priv *pd = vb2_get_drv_priv(q); - struct vb2_buffer *vb; - - spin_lock_irq(&pd->lock); - while (!list_empty(&pd->dmaq)) { - vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry); - list_del(&vb->done_entry); - vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); - } - spin_unlock_irq(&pd->lock); - msleep(45); /* irq hendler will stop the hardware */ - return 0; -} - -static void -dt3155_buf_queue(struct vb2_buffer *vb) -{ - struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue); - - /* pd->q->streaming = 1 when dt3155_buf_queue() is invoked */ - spin_lock_irq(&pd->lock); - if (pd->curr_buf) - list_add_tail(&vb->done_entry, &pd->dmaq); - else { - pd->curr_buf = vb; - dt3155_start_acq(pd); - } - spin_unlock_irq(&pd->lock); -} -/* - * end driver-specific callbacks - */ - -const struct vb2_ops q_ops = { - .queue_setup = dt3155_queue_setup, - .wait_prepare = dt3155_wait_prepare, - .wait_finish = dt3155_wait_finish, - .buf_prepare = dt3155_buf_prepare, - .start_streaming = dt3155_start_streaming, - .stop_streaming = dt3155_stop_streaming, - .buf_queue = dt3155_buf_queue, -}; - -static irqreturn_t -dt3155_irq_handler_even(int irq, void *dev_id) -{ - struct dt3155_priv *ipd = dev_id; - struct vb2_buffer *ivb; - dma_addr_t dma_addr; - u32 tmp; - - tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD); - if (!tmp) - return IRQ_NONE; /* not our irq */ - if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) { - iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START, - ipd->regs + INT_CSR); - ipd->field_count++; - return IRQ_HANDLED; /* start of field irq */ - } - if ((tmp & FLD_START) && (tmp & FLD_END_ODD)) - ipd->stats.start_before_end++; - /* check for corrupted fields */ -/* write_i2c_reg(ipd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); */ -/* write_i2c_reg(ipd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); */ - tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD); - if (tmp) { - ipd->stats.corrupted_fields++; - iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | - FLD_DN_ODD | FLD_DN_EVEN | - CAP_CONT_EVEN | CAP_CONT_ODD, - ipd->regs + CSR1); - mmiowb(); - } - - spin_lock(&ipd->lock); - if (ipd->curr_buf) { - do_gettimeofday(&ipd->curr_buf->v4l2_buf.timestamp); - ipd->curr_buf->v4l2_buf.sequence = (ipd->field_count) >> 1; - vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE); - } - - if (!ipd->q->streaming || list_empty(&ipd->dmaq)) - goto stop_dma; - ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry); - list_del(&ivb->done_entry); - ipd->curr_buf = ivb; - dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0); - iowrite32(dma_addr, ipd->regs + EVEN_DMA_START); - iowrite32(dma_addr + img_width, ipd->regs + ODD_DMA_START); - iowrite32(img_width, ipd->regs + EVEN_DMA_STRIDE); - iowrite32(img_width, ipd->regs + ODD_DMA_STRIDE); - mmiowb(); - /* enable interrupts, clear all irq flags */ - iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | - FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR); - spin_unlock(&ipd->lock); - return IRQ_HANDLED; - -stop_dma: - ipd->curr_buf = NULL; - /* stop the board */ - write_i2c_reg_nowait(ipd->regs, CSR2, ipd->csr2); - iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | - FLD_DN_ODD | FLD_DN_EVEN, ipd->regs + CSR1); - /* disable interrupts, clear all irq flags */ - iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR); - spin_unlock(&ipd->lock); - return IRQ_HANDLED; -} - -static int -dt3155_open(struct file *filp) -{ - int ret = 0; - struct dt3155_priv *pd = video_drvdata(filp); - - if (!pd->users) { - pd->q = kzalloc(sizeof(*pd->q), GFP_KERNEL); - if (!pd->q) { - ret = -ENOMEM; - goto err_alloc_queue; - } - pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - pd->q->io_modes = VB2_READ | VB2_MMAP; - pd->q->ops = &q_ops; - pd->q->mem_ops = &vb2_dma_contig_memops; - pd->q->drv_priv = pd; - pd->curr_buf = NULL; - pd->field_count = 0; - vb2_queue_init(pd->q); /* cannot fail */ - INIT_LIST_HEAD(&pd->dmaq); - spin_lock_init(&pd->lock); - /* disable all irqs, clear all irq flags */ - iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, - pd->regs + INT_CSR); - ret = request_irq(pd->pdev->irq, dt3155_irq_handler_even, - IRQF_SHARED, DT3155_NAME, pd); - if (ret) - goto err_request_irq; - } - pd->users++; - return 0; /* success */ -err_request_irq: - kfree(pd->q); - pd->q = NULL; -err_alloc_queue: - return ret; -} - -static int -dt3155_release(struct file *filp) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - pd->users--; - BUG_ON(pd->users < 0); - if (!pd->users) { - vb2_queue_release(pd->q); - free_irq(pd->pdev->irq, pd); - if (pd->q->alloc_ctx[0]) - vb2_dma_contig_cleanup_ctx(pd->q->alloc_ctx[0]); - kfree(pd->q); - pd->q = NULL; - } - return 0; -} - -static ssize_t -dt3155_read(struct file *filp, char __user *user, size_t size, loff_t *loff) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK); -} - -static unsigned int -dt3155_poll(struct file *filp, struct poll_table_struct *polltbl) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_poll(pd->q, filp, polltbl); -} - -static int -dt3155_mmap(struct file *filp, struct vm_area_struct *vma) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_mmap(pd->q, vma); -} - -static const struct v4l2_file_operations dt3155_fops = { - .owner = THIS_MODULE, - .open = dt3155_open, - .release = dt3155_release, - .read = dt3155_read, - .poll = dt3155_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = dt3155_mmap, -}; - -static int -dt3155_ioc_streamon(struct file *filp, void *p, enum v4l2_buf_type type) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_streamon(pd->q, type); -} - -static int -dt3155_ioc_streamoff(struct file *filp, void *p, enum v4l2_buf_type type) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_streamoff(pd->q, type); -} - -static int -dt3155_ioc_querycap(struct file *filp, void *p, struct v4l2_capability *cap) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - strcpy(cap->driver, DT3155_NAME); - strcpy(cap->card, DT3155_NAME " frame grabber"); - sprintf(cap->bus_info, "PCI:%s", pci_name(pd->pdev)); - cap->version = - KERNEL_VERSION(DT3155_VER_MAJ, DT3155_VER_MIN, DT3155_VER_EXT); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - DT3155_CAPTURE_METHOD; - return 0; -} - -static int -dt3155_ioc_enum_fmt_vid_cap(struct file *filp, void *p, struct v4l2_fmtdesc *f) -{ - if (f->index >= NUM_OF_FORMATS) - return -EINVAL; - *f = frame_std[f->index]; - return 0; -} - -static int -dt3155_ioc_g_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) -{ - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - f->fmt.pix.width = img_width; - f->fmt.pix.height = img_height; - f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = f->fmt.pix.width; - f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height; - f->fmt.pix.colorspace = 0; - f->fmt.pix.priv = 0; - return 0; -} - -static int -dt3155_ioc_try_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) -{ - if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (f->fmt.pix.width == img_width && - f->fmt.pix.height == img_height && - f->fmt.pix.pixelformat == V4L2_PIX_FMT_GREY && - f->fmt.pix.field == V4L2_FIELD_NONE && - f->fmt.pix.bytesperline == f->fmt.pix.width && - f->fmt.pix.sizeimage == f->fmt.pix.width * f->fmt.pix.height) - return 0; - else - return -EINVAL; -} - -static int -dt3155_ioc_s_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) -{ - return dt3155_ioc_g_fmt_vid_cap(filp, p, f); -} - -static int -dt3155_ioc_reqbufs(struct file *filp, void *p, struct v4l2_requestbuffers *b) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_reqbufs(pd->q, b); -} - -static int -dt3155_ioc_querybuf(struct file *filp, void *p, struct v4l2_buffer *b) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_querybuf(pd->q, b); -} - -static int -dt3155_ioc_qbuf(struct file *filp, void *p, struct v4l2_buffer *b) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_qbuf(pd->q, b); -} - -static int -dt3155_ioc_dqbuf(struct file *filp, void *p, struct v4l2_buffer *b) -{ - struct dt3155_priv *pd = video_drvdata(filp); - - return vb2_dqbuf(pd->q, b, filp->f_flags & O_NONBLOCK); -} - -static int -dt3155_ioc_querystd(struct file *filp, void *p, v4l2_std_id *norm) -{ - *norm = DT3155_CURRENT_NORM; - return 0; -} - -static int -dt3155_ioc_g_std(struct file *filp, void *p, v4l2_std_id *norm) -{ - *norm = DT3155_CURRENT_NORM; - return 0; -} - -static int -dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id *norm) -{ - if (*norm & DT3155_CURRENT_NORM) - return 0; - return -EINVAL; -} - -static int -dt3155_ioc_enum_input(struct file *filp, void *p, struct v4l2_input *input) -{ - if (input->index) - return -EINVAL; - strcpy(input->name, "Coax in"); - input->type = V4L2_INPUT_TYPE_CAMERA; - /* - * FIXME: input->std = 0 according to v4l2 API - * VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_QUERYSTD and VIDIOC_ENUMSTD - * should return -EINVAL - */ - input->std = DT3155_CURRENT_NORM; - input->status = 0;/* FIXME: add sync detection & V4L2_IN_ST_NO_H_LOCK */ - return 0; -} - -static int -dt3155_ioc_g_input(struct file *filp, void *p, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int -dt3155_ioc_s_input(struct file *filp, void *p, unsigned int i) -{ - if (i) - return -EINVAL; - return 0; -} - -static int -dt3155_ioc_g_parm(struct file *filp, void *p, struct v4l2_streamparm *parms) -{ - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parms->parm.capture.capturemode = 0; - parms->parm.capture.timeperframe.numerator = 1001; - parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000; - parms->parm.capture.extendedmode = 0; - parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */ - return 0; -} - -static int -dt3155_ioc_s_parm(struct file *filp, void *p, struct v4l2_streamparm *parms) -{ - if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - parms->parm.capture.capturemode = 0; - parms->parm.capture.timeperframe.numerator = 1001; - parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000; - parms->parm.capture.extendedmode = 0; - parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */ - return 0; -} - -static const struct v4l2_ioctl_ops dt3155_ioctl_ops = { - .vidioc_streamon = dt3155_ioc_streamon, - .vidioc_streamoff = dt3155_ioc_streamoff, - .vidioc_querycap = dt3155_ioc_querycap, -/* - .vidioc_g_priority = dt3155_ioc_g_priority, - .vidioc_s_priority = dt3155_ioc_s_priority, -*/ - .vidioc_enum_fmt_vid_cap = dt3155_ioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = dt3155_ioc_try_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = dt3155_ioc_g_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = dt3155_ioc_s_fmt_vid_cap, - .vidioc_reqbufs = dt3155_ioc_reqbufs, - .vidioc_querybuf = dt3155_ioc_querybuf, - .vidioc_qbuf = dt3155_ioc_qbuf, - .vidioc_dqbuf = dt3155_ioc_dqbuf, - .vidioc_querystd = dt3155_ioc_querystd, - .vidioc_g_std = dt3155_ioc_g_std, - .vidioc_s_std = dt3155_ioc_s_std, - .vidioc_enum_input = dt3155_ioc_enum_input, - .vidioc_g_input = dt3155_ioc_g_input, - .vidioc_s_input = dt3155_ioc_s_input, -/* - .vidioc_queryctrl = dt3155_ioc_queryctrl, - .vidioc_g_ctrl = dt3155_ioc_g_ctrl, - .vidioc_s_ctrl = dt3155_ioc_s_ctrl, - .vidioc_querymenu = dt3155_ioc_querymenu, - .vidioc_g_ext_ctrls = dt3155_ioc_g_ext_ctrls, - .vidioc_s_ext_ctrls = dt3155_ioc_s_ext_ctrls, -*/ - .vidioc_g_parm = dt3155_ioc_g_parm, - .vidioc_s_parm = dt3155_ioc_s_parm, -/* - .vidioc_cropcap = dt3155_ioc_cropcap, - .vidioc_g_crop = dt3155_ioc_g_crop, - .vidioc_s_crop = dt3155_ioc_s_crop, - .vidioc_enum_framesizes = dt3155_ioc_enum_framesizes, - .vidioc_enum_frameintervals = dt3155_ioc_enum_frameintervals, -*/ -}; - -static int __devinit -dt3155_init_board(struct pci_dev *pdev) -{ - struct dt3155_priv *pd = pci_get_drvdata(pdev); - void *buf_cpu; - dma_addr_t buf_dma; - int i; - u8 tmp; - - pci_set_master(pdev); /* dt3155 needs it */ - - /* resetting the adapter */ - iowrite32(FLD_CRPT_ODD | FLD_CRPT_EVEN | FLD_DN_ODD | FLD_DN_EVEN, - pd->regs + CSR1); - mmiowb(); - msleep(20); - - /* initializing adaper registers */ - iowrite32(FIFO_EN | SRST, pd->regs + CSR1); - mmiowb(); - iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); - iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); - iowrite32(0x00000020, pd->regs + FIFO_TRIGER); - iowrite32(0x00000103, pd->regs + XFER_MODE); - iowrite32(0, pd->regs + RETRY_WAIT_CNT); - iowrite32(0, pd->regs + INT_CSR); - iowrite32(1, pd->regs + EVEN_FLD_MASK); - iowrite32(1, pd->regs + ODD_FLD_MASK); - iowrite32(0, pd->regs + MASK_LENGTH); - iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT); - iowrite32(0x01010101, pd->regs + IIC_CLK_DUR); - mmiowb(); - - /* verifying that we have a DT3155 board (not just a SAA7116 chip) */ - read_i2c_reg(pd->regs, DT_ID, &tmp); - if (tmp != DT3155_ID) - return -ENODEV; - - /* initialize AD LUT */ - write_i2c_reg(pd->regs, AD_ADDR, 0); - for (i = 0; i < 256; i++) - write_i2c_reg(pd->regs, AD_LUT, i); - - /* initialize ADC references */ - /* FIXME: pos_ref & neg_ref depend on VT_50HZ */ - write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); - write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); - write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF); - write_i2c_reg(pd->regs, AD_CMD, 34); - write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF); - write_i2c_reg(pd->regs, AD_CMD, 0); - - /* initialize PM LUT */ - write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM); - for (i = 0; i < 256; i++) { - write_i2c_reg(pd->regs, PM_LUT_ADDR, i); - write_i2c_reg(pd->regs, PM_LUT_DATA, i); - } - write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL); - for (i = 0; i < 256; i++) { - write_i2c_reg(pd->regs, PM_LUT_ADDR, i); - write_i2c_reg(pd->regs, PM_LUT_DATA, i); - } - write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ - - /* select chanel 1 for input and set sync level */ - write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); - write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); - - /* allocate memory, and initialize the DMA machine */ - buf_cpu = dma_alloc_coherent(&pdev->dev, DT3155_BUF_SIZE, &buf_dma, - GFP_KERNEL); - if (!buf_cpu) - return -ENOMEM; - iowrite32(buf_dma, pd->regs + EVEN_DMA_START); - iowrite32(buf_dma, pd->regs + ODD_DMA_START); - iowrite32(0, pd->regs + EVEN_DMA_STRIDE); - iowrite32(0, pd->regs + ODD_DMA_STRIDE); - - /* Perform a pseudo even field acquire */ - iowrite32(FIFO_EN | SRST | CAP_CONT_ODD, pd->regs + CSR1); - write_i2c_reg(pd->regs, CSR2, pd->csr2 | SYNC_SNTL); - write_i2c_reg(pd->regs, CONFIG, pd->config); - write_i2c_reg(pd->regs, EVEN_CSR, CSR_SNGL); - write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | SYNC_SNTL); - msleep(100); - read_i2c_reg(pd->regs, CSR2, &tmp); - write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE); - write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE); - write_i2c_reg(pd->regs, CSR2, pd->csr2); - iowrite32(FIFO_EN | SRST | FLD_DN_EVEN | FLD_DN_ODD, pd->regs + CSR1); - - /* deallocate memory */ - dma_free_coherent(&pdev->dev, DT3155_BUF_SIZE, buf_cpu, buf_dma); - if (tmp & BUSY_EVEN) - return -EIO; - return 0; -} - -static struct video_device dt3155_vdev = { - .name = DT3155_NAME, - .fops = &dt3155_fops, - .ioctl_ops = &dt3155_ioctl_ops, - .minor = -1, - .release = video_device_release, - .tvnorms = DT3155_CURRENT_NORM, - .current_norm = DT3155_CURRENT_NORM, -}; - -/* same as in drivers/base/dma-coherent.c */ -struct dma_coherent_mem { - void *virt_base; - dma_addr_t device_base; - int size; - int flags; - unsigned long *bitmap; -}; - -static int __devinit -dt3155_alloc_coherent(struct device *dev, size_t size, int flags) -{ - struct dma_coherent_mem *mem; - dma_addr_t dev_base; - int pages = size >> PAGE_SHIFT; - int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); - - if ((flags & DMA_MEMORY_MAP) == 0) - goto out; - if (!size) - goto out; - if (dev->dma_mem) - goto out; - - mem = kzalloc(sizeof(*mem), GFP_KERNEL); - if (!mem) - goto out; - mem->virt_base = dma_alloc_coherent(dev, size, &dev_base, - DT3155_COH_FLAGS); - if (!mem->virt_base) - goto err_alloc_coherent; - mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); - if (!mem->bitmap) - goto err_bitmap; - - /* coherent_dma_mask is already set to 32 bits */ - mem->device_base = dev_base; - mem->size = pages; - mem->flags = flags; - dev->dma_mem = mem; - return DMA_MEMORY_MAP; - -err_bitmap: - dma_free_coherent(dev, size, mem->virt_base, dev_base); -err_alloc_coherent: - kfree(mem); -out: - return 0; -} - -static void __devexit -dt3155_free_coherent(struct device *dev) -{ - struct dma_coherent_mem *mem = dev->dma_mem; - - if (!mem) - return; - dev->dma_mem = NULL; - dma_free_coherent(dev, mem->size << PAGE_SHIFT, - mem->virt_base, mem->device_base); - kfree(mem->bitmap); - kfree(mem); -} - -static int __devinit -dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - int err; - struct dt3155_priv *pd; - - err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (err) - return -ENODEV; - err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (err) - return -ENODEV; - pd = kzalloc(sizeof(*pd), GFP_KERNEL); - if (!pd) - return -ENOMEM; - pd->vdev = video_device_alloc(); - if (!pd->vdev) - goto err_video_device_alloc; - *pd->vdev = dt3155_vdev; - pci_set_drvdata(pdev, pd); /* for use in dt3155_remove() */ - video_set_drvdata(pd->vdev, pd); /* for use in video_fops */ - pd->users = 0; - pd->pdev = pdev; - INIT_LIST_HEAD(&pd->dmaq); - mutex_init(&pd->mux); - pd->vdev->lock = &pd->mux; /* for locking v4l2_file_operations */ - spin_lock_init(&pd->lock); - pd->csr2 = csr2_init; - pd->config = config_init; - err = pci_enable_device(pdev); - if (err) - goto err_enable_dev; - err = pci_request_region(pdev, 0, pci_name(pdev)); - if (err) - goto err_req_region; - pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0)); - if (!pd->regs) - err = -ENOMEM; - goto err_pci_iomap; - err = dt3155_init_board(pdev); - if (err) - goto err_init_board; - err = video_register_device(pd->vdev, VFL_TYPE_GRABBER, -1); - if (err) - goto err_init_board; - if (dt3155_alloc_coherent(&pdev->dev, DT3155_CHUNK_SIZE, - DMA_MEMORY_MAP)) - dev_info(&pdev->dev, "preallocated 8 buffers\n"); - dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev->minor); - return 0; /* success */ - -err_init_board: - pci_iounmap(pdev, pd->regs); -err_pci_iomap: - pci_release_region(pdev, 0); -err_req_region: - pci_disable_device(pdev); -err_enable_dev: - video_device_release(pd->vdev); -err_video_device_alloc: - kfree(pd); - return err; -} - -static void __devexit -dt3155_remove(struct pci_dev *pdev) -{ - struct dt3155_priv *pd = pci_get_drvdata(pdev); - - dt3155_free_coherent(&pdev->dev); - video_unregister_device(pd->vdev); - pci_iounmap(pdev, pd->regs); - pci_release_region(pdev, 0); - pci_disable_device(pdev); - /* - * video_device_release() is invoked automatically - * see: struct video_device dt3155_vdev - */ - kfree(pd); -} - -static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { - { PCI_DEVICE(DT3155_VENDOR_ID, DT3155_DEVICE_ID) }, - { 0, /* zero marks the end */ }, -}; -MODULE_DEVICE_TABLE(pci, pci_ids); - -static struct pci_driver pci_driver = { - .name = DT3155_NAME, - .id_table = pci_ids, - .probe = dt3155_probe, - .remove = __devexit_p(dt3155_remove), -}; - -static int __init -dt3155_init_module(void) -{ - return pci_register_driver(&pci_driver); -} - -static void __exit -dt3155_exit_module(void) -{ - pci_unregister_driver(&pci_driver); -} - -module_init(dt3155_init_module); -module_exit(dt3155_exit_module); - -MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber"); -MODULE_AUTHOR("Marin Mitov "); -MODULE_VERSION(DT3155_VERSION); -MODULE_LICENSE("GPL"); diff --git a/drivers/staging/dt3155v4l/dt3155v4l.h b/drivers/staging/dt3155v4l/dt3155v4l.h deleted file mode 100644 index 2e4f89d402e4..000000000000 --- a/drivers/staging/dt3155v4l/dt3155v4l.h +++ /dev/null @@ -1,212 +0,0 @@ -/*************************************************************************** - * Copyright (C) 2006-2010 by Marin Mitov * - * mitov@issp.bas.bg * - * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation; either version 2 of the License, or * - * (at your option) any later version. * - * * - * This program is distributed in the hope that it will be useful, * - * but WITHOUT ANY WARRANTY; without even the implied warranty of * - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * - * GNU General Public License for more details. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program; if not, write to the * - * Free Software Foundation, Inc., * - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * - ***************************************************************************/ - -/* DT3155 header file */ -#ifndef _DT3155_H_ -#define _DT3155_H_ - -#ifdef __KERNEL__ - -#include -#include - -#define DT3155_NAME "dt3155" -#define DT3155_VER_MAJ 1 -#define DT3155_VER_MIN 1 -#define DT3155_VER_EXT 0 -#define DT3155_VERSION __stringify(DT3155_VER_MAJ) "." \ - __stringify(DT3155_VER_MIN) "." \ - __stringify(DT3155_VER_EXT) - -/* DT3155 Base Register offsets (memory mapped) */ -#define EVEN_DMA_START 0x00 -#define ODD_DMA_START 0x0C -#define EVEN_DMA_STRIDE 0x18 -#define ODD_DMA_STRIDE 0x24 -#define EVEN_PIXEL_FMT 0x30 -#define ODD_PIXEL_FMT 0x34 -#define FIFO_TRIGER 0x38 -#define XFER_MODE 0x3C -#define CSR1 0x40 -#define RETRY_WAIT_CNT 0x44 -#define INT_CSR 0x48 -#define EVEN_FLD_MASK 0x4C -#define ODD_FLD_MASK 0x50 -#define MASK_LENGTH 0x54 -#define FIFO_FLAG_CNT 0x58 -#define IIC_CLK_DUR 0x5C -#define IIC_CSR1 0x60 -#define IIC_CSR2 0x64 - -/* DT3155 Internal Registers indexes (i2c/IIC mapped) */ -#define CSR2 0x10 -#define EVEN_CSR 0x11 -#define ODD_CSR 0x12 -#define CONFIG 0x13 -#define DT_ID 0x1F -#define X_CLIP_START 0x20 -#define Y_CLIP_START 0x22 -#define X_CLIP_END 0x24 -#define Y_CLIP_END 0x26 -#define AD_ADDR 0x30 -#define AD_LUT 0x31 -#define AD_CMD 0x32 -#define DIG_OUT 0x40 -#define PM_LUT_ADDR 0x50 -#define PM_LUT_DATA 0x51 - -/* AD command register values */ -#define AD_CMD_REG 0x00 -#define AD_POS_REF 0x01 -#define AD_NEG_REF 0x02 - -/* CSR1 bit masks */ -#define CRPT_DIS 0x00004000 -#define FLD_CRPT_ODD 0x00000200 -#define FLD_CRPT_EVEN 0x00000100 -#define FIFO_EN 0x00000080 -#define SRST 0x00000040 -#define FLD_DN_ODD 0x00000020 -#define FLD_DN_EVEN 0x00000010 -/* These should not be used. - * Use CAP_CONT_ODD/EVEN instead -#define CAP_SNGL_ODD 0x00000008 -#define CAP_SNGL_EVEN 0x00000004 -*/ -#define CAP_CONT_ODD 0x00000002 -#define CAP_CONT_EVEN 0x00000001 - -/* INT_CSR bit masks */ -#define FLD_START_EN 0x00000400 -#define FLD_END_ODD_EN 0x00000200 -#define FLD_END_EVEN_EN 0x00000100 -#define FLD_START 0x00000004 -#define FLD_END_ODD 0x00000002 -#define FLD_END_EVEN 0x00000001 - -/* IIC_CSR1 bit masks */ -#define DIRECT_ABORT 0x00000200 - -/* IIC_CSR2 bit masks */ -#define NEW_CYCLE 0x01000000 -#define DIR_RD 0x00010000 -#define IIC_READ 0x01010000 -#define IIC_WRITE 0x01000000 - -/* CSR2 bit masks */ -#define DISP_PASS 0x40 -#define BUSY_ODD 0x20 -#define BUSY_EVEN 0x10 -#define SYNC_PRESENT 0x08 -#define VT_50HZ 0x04 -#define SYNC_SNTL 0x02 -#define CHROM_FILT 0x01 -#define VT_60HZ 0x00 - -/* CSR_EVEN/ODD bit masks */ -#define CSR_ERROR 0x04 -#define CSR_SNGL 0x02 -#define CSR_DONE 0x01 - -/* CONFIG bit masks */ -#define PM_LUT_PGM 0x80 -#define PM_LUT_SEL 0x40 -#define CLIP_EN 0x20 -#define HSCALE_EN 0x10 -#define EXT_TRIG_UP 0x0C -#define EXT_TRIG_DOWN 0x04 -#define ACQ_MODE_NEXT 0x02 -#define ACQ_MODE_ODD 0x01 -#define ACQ_MODE_EVEN 0x00 - -/* AD_CMD bit masks */ -#define VIDEO_CNL_1 0x00 -#define VIDEO_CNL_2 0x40 -#define VIDEO_CNL_3 0x80 -#define VIDEO_CNL_4 0xC0 -#define SYNC_CNL_1 0x00 -#define SYNC_CNL_2 0x10 -#define SYNC_CNL_3 0x20 -#define SYNC_CNL_4 0x30 -#define SYNC_LVL_1 0x00 -#define SYNC_LVL_2 0x04 -#define SYNC_LVL_3 0x08 -#define SYNC_LVL_4 0x0C - -/* DT3155 identificator */ -#define DT3155_ID 0x20 - -#ifdef CONFIG_DT3155_CCIR -#define DMA_STRIDE 768 -#else -#define DMA_STRIDE 640 -#endif - -/** - * struct dt3155_stats - statistics structure - * - * @free_bufs_empty: no free image buffers - * @corrupted_fields: corrupted fields - * @dma_map_failed: dma mapping failed - * @start_before_end: new started before old ended - */ -struct dt3155_stats { - int free_bufs_empty; - int corrupted_fields; - int dma_map_failed; - int start_before_end; -}; - -/* per board private data structure */ -/** - * struct dt3155_priv - private data structure - * - * @vdev: pointer to video_device structure - * @pdev: pointer to pci_dev structure - * @q pointer to vb2_queue structure - * @curr_buf: pointer to curren buffer - * @mux: mutex to protect the instance - * @dmaq queue for dma buffers - * @lock spinlock for dma queue - * @field_count fields counter - * @stats: statistics structure - * @users open count - * @regs: local copy of mmio base register - * @csr2: local copy of csr2 register - * @config: local copy of config register - */ -struct dt3155_priv { - struct video_device *vdev; - struct pci_dev *pdev; - struct vb2_queue *q; - struct vb2_buffer *curr_buf; - struct mutex mux; - struct list_head dmaq; - spinlock_t lock; - unsigned int field_count; - struct dt3155_stats stats; - void __iomem *regs; - int users; - u8 csr2, config; -}; - -#endif /* __KERNEL__ */ - -#endif /* _DT3155_H_ */ diff --git a/drivers/staging/easycap/Kconfig b/drivers/staging/easycap/Kconfig deleted file mode 100644 index a425a6f9cdca..000000000000 --- a/drivers/staging/easycap/Kconfig +++ /dev/null @@ -1,30 +0,0 @@ -config EASYCAP - tristate "EasyCAP USB ID 05e1:0408 support" - depends on USB && VIDEO_DEV && SND - select SND_PCM - - ---help--- - This is an integrated audio/video driver for EasyCAP cards with - USB ID 05e1:0408. It supports two hardware variants: - - * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60, - having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R) - - * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled - 1, 2, 3, 4 and an unlabelled input cable for a microphone. - - To compile this driver as a module, choose M here: the - module will be called easycap - -config EASYCAP_DEBUG - bool "Enable EasyCAP driver debugging" - depends on EASYCAP - - ---help--- - This option enables debug printouts - - To enable debug, pass the debug level to the debug module - parameter: - - modprobe easycap debug=[0..9] - diff --git a/drivers/staging/easycap/Makefile b/drivers/staging/easycap/Makefile deleted file mode 100644 index a34e75f59c18..000000000000 --- a/drivers/staging/easycap/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -easycap-objs := easycap_main.o -easycap-objs += easycap_low.o -easycap-objs += easycap_ioctl.o -easycap-objs += easycap_settings.o -easycap-objs += easycap_testcard.o -easycap-objs += easycap_sound.o -obj-$(CONFIG_EASYCAP) += easycap.o - -ccflags-y := -Wall - diff --git a/drivers/staging/easycap/README b/drivers/staging/easycap/README deleted file mode 100644 index 796b032384bd..000000000000 --- a/drivers/staging/easycap/README +++ /dev/null @@ -1,141 +0,0 @@ - - *********************************************************** - * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60 * - * and * - * EasyCAP002 4-Channel USB 2.0 DVR * - *********************************************************** - Mike Thomas - - - -SUPPORTED HARDWARE ------------------- - -This driver is intended for use with hardware having USB ID 05e1:0408. -Two kinds of EasyCAP have this USB ID, namely: - - * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60, - having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R) - - * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled - 1, 2, 3, 4 and an unlabelled input cable for a microphone. - - -BUILD OPTIONS AND DEPENDENCIES ------------------------------- - -Unless EASYCAP_DEBUG is defined during compilation it will not be possible -to select a debug level at the time of module installation. - - -KNOWN RUNTIME ISSUES --------------------- - -(1) Intentionally, this driver will not stream material which is unambiguously -identified by the hardware as copy-protected. Normal video output will be -present for about a minute but will then freeze when this situation arises. - -(2) The controls for luminance, contrast, saturation, hue and volume may not -always work properly. - -(3) Reduced-resolution S-Video seems to suffer from moire artefacts. - - -INPUT NUMBERING ---------------- - -For the EasyCAP with S-VIDEO input cable the driver regards a request for -inputs numbered 0 or 1 as referring to CVBS and a request for input -numbered 5 as referring to S-VIDEO. - -For the EasyCAP with four CVBS inputs the driver expects to be asked for -any one of inputs numbered 1,2,3,4. If input 0 is asked for, it is -interpreted as input 1. - - -MODULE PARAMETERS ------------------ - -Three module parameters are defined: - -debug the easycap module is configured at diagnostic level n (0 to 9) -gain audio gain level n (0 to 31, default is 16) -bars whether to display testcard bars when incoming video signal is lost - 0 => no, 1 => yes (default) - - -SUPPORTED TV STANDARDS AND RESOLUTIONS --------------------------------------- - -The following TV standards are natively supported by the hardware and are -usable as (for example) the "norm=" parameter in the mplayer command: - - PAL_BGHIN, NTSC_N_443, - PAL_Nc, NTSC_N, - SECAM, NTSC_M, NTSC_M_JP, - PAL_60, NTSC_443, - PAL_M. - -In addition, the driver offers "custom" pseudo-standards with a framerate -which is 20% of the usual framerate. These pseudo-standards are named: - - PAL_BGHIN_SLOW, NTSC_N_443_SLOW, - PAL_Nc_SLOW, NTSC_N_SLOW, - SECAM_SLOW, NTSC_M_SLOW, NTSC_M_JP_SLOW, - PAL_60_SLOW, NTSC_443_SLOW, - PAL_M_SLOW. - - -The available picture sizes are: - - at 25 frames per second: 720x576, 704x576, 640x480, 360x288, 320x240; - at 30 frames per second: 720x480, 640x480, 360x240, 320x240. - - -WHAT'S TESTED AND WHAT'S NOT ----------------------------- - -This driver is known to work with mplayer, mencoder, tvtime, zoneminder, -xawtv, gstreamer and sufficiently recent versions of vlc. An interface -to ffmpeg is implemented, but serious audio-video synchronization problems -remain. - -The driver is designed to support all the TV standards accepted by the -hardware, but as yet it has actually been tested on only a few of these. - -I have been unable to test and calibrate the S-video input myself because I -do not possess any equipment with S-video output. - - -UDEV RULES ----------- - -In order that the special files /dev/easycap0 and /dev/easysnd1 are created -with conveniently relaxed permissions when the EasyCAP is plugged in, a file -is preferably to be provided in directory /etc/udev/rules.d with content: - -ACTION!="add|change", GOTO="easycap_rules_end" -ATTRS{idVendor}=="05e1", ATTRS{idProduct}=="0408", \ - MODE="0666", OWNER="root", GROUP="root" -LABEL="easycap_rules_end" - - -MODPROBE CONFIGURATION ----------------------- - -The easycap module is in competition with the module snd-usb-audio for the -EasyCAP's audio channel, and its installation can be aided by providing a -file in directory /etc/modprobe.d with content: - -options easycap gain=16 bars=1 -install easycap /sbin/rmmod snd-usb-audio; /sbin/modprobe --ignore-install easycap - - -ACKNOWLEGEMENTS AND REFERENCES ------------------------------- -This driver makes use of information contained in the Syntek Semicon DC-1125 -Driver, presently maintained at http://sourceforge.net/projects/syntekdriver/ -by Nicolas Vivien. Particularly useful has been a patch to the latter driver -provided by Ivor Hewitt in January 2009. The NTSC implementation is taken -from the work of Ben Trask. - diff --git a/drivers/staging/easycap/easycap.h b/drivers/staging/easycap/easycap.h deleted file mode 100644 index 7b256a948c27..000000000000 --- a/drivers/staging/easycap/easycap.h +++ /dev/null @@ -1,594 +0,0 @@ -/***************************************************************************** -* * -* easycap.h * -* * -*****************************************************************************/ -/* - * - * Copyright (C) 2010 R.M. Thomas - * - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * THE FOLLOWING PARAMETERS ARE UNDEFINED: - * - * EASYCAP_DEBUG - * - * IF REQUIRED THEY MUST BE EXTERNALLY DEFINED, FOR EXAMPLE AS COMPILER - * OPTIONS. - */ -/*---------------------------------------------------------------------------*/ - -#ifndef __EASYCAP_H__ -#define __EASYCAP_H__ - -/*---------------------------------------------------------------------------*/ -/* - * THESE ARE NORMALLY DEFINED - */ -/*---------------------------------------------------------------------------*/ -#define PATIENCE 500 -#define PERSEVERE -/*---------------------------------------------------------------------------*/ -/* - * THESE ARE FOR MAINTENANCE ONLY - NORMALLY UNDEFINED: - */ -/*---------------------------------------------------------------------------*/ -#undef EASYCAP_TESTCARD -/*---------------------------------------------------------------------------*/ -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/*---------------------------------------------------------------------------*/ -/* VENDOR, PRODUCT: Syntek Semiconductor Co., Ltd - * - * EITHER EasyCAP USB 2.0 Video Adapter with Audio, Model No. DC60 - * with input cabling: AUDIO(L), AUDIO(R), CVBS, S-VIDEO. - * - * OR EasyCAP 4CHANNEL USB 2.0 DVR, Model No. EasyCAP002 - * with input cabling: MICROPHONE, CVBS1, CVBS2, CVBS3, CVBS4. - */ -/*---------------------------------------------------------------------------*/ -#define USB_EASYCAP_VENDOR_ID 0x05e1 -#define USB_EASYCAP_PRODUCT_ID 0x0408 - -#define EASYCAP_DRIVER_VERSION "0.9.01" -#define EASYCAP_DRIVER_DESCRIPTION "easycapdc60" - -#define USB_SKEL_MINOR_BASE 192 -#define DONGLE_MANY 8 -#define INPUT_MANY 6 -/*---------------------------------------------------------------------------*/ -/* - * DEFAULT LUMINANCE, CONTRAST, SATURATION AND HUE - */ -/*---------------------------------------------------------------------------*/ -#define SAA_0A_DEFAULT 0x7F -#define SAA_0B_DEFAULT 0x3F -#define SAA_0C_DEFAULT 0x2F -#define SAA_0D_DEFAULT 0x00 -/*---------------------------------------------------------------------------*/ -/* - * VIDEO STREAMING PARAMETERS: - * USB 2.0 PROVIDES FOR HIGH-BANDWIDTH ENDPOINTS WITH AN UPPER LIMIT - * OF 3072 BYTES PER MICROFRAME for wMaxPacketSize. - */ -/*---------------------------------------------------------------------------*/ -#define VIDEO_ISOC_BUFFER_MANY 16 -#define VIDEO_ISOC_ORDER 3 -#define VIDEO_ISOC_FRAMESPERDESC ((unsigned int) 1 << VIDEO_ISOC_ORDER) -#define USB_2_0_MAXPACKETSIZE 3072 -#if (USB_2_0_MAXPACKETSIZE > PAGE_SIZE) -#error video_isoc_buffer[.] will not be big enough -#endif -#define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY -#define VIDEO_LOST_TOLERATE 50 -/*---------------------------------------------------------------------------*/ -/* - * VIDEO BUFFERS - */ -/*---------------------------------------------------------------------------*/ -#define FIELD_BUFFER_SIZE (203 * PAGE_SIZE) -#define FRAME_BUFFER_SIZE (405 * PAGE_SIZE) -#define FIELD_BUFFER_MANY 4 -#define FRAME_BUFFER_MANY 6 -/*---------------------------------------------------------------------------*/ -/* - * AUDIO STREAMING PARAMETERS - */ -/*---------------------------------------------------------------------------*/ -#define AUDIO_ISOC_BUFFER_MANY 16 -#define AUDIO_ISOC_ORDER 1 -#define AUDIO_ISOC_FRAMESPERDESC 32 -#define AUDIO_ISOC_BUFFER_SIZE (PAGE_SIZE << AUDIO_ISOC_ORDER) -/*---------------------------------------------------------------------------*/ -/* - * AUDIO BUFFERS - */ -/*---------------------------------------------------------------------------*/ -#define AUDIO_FRAGMENT_MANY 32 -#define PAGES_PER_AUDIO_FRAGMENT 4 -/*---------------------------------------------------------------------------*/ -/* - * IT IS ESSENTIAL THAT EVEN-NUMBERED STANDARDS ARE 25 FRAMES PER SECOND, - * ODD-NUMBERED STANDARDS ARE 30 FRAMES PER SECOND. - * THE NUMBERING OF STANDARDS MUST NOT BE CHANGED WITHOUT DUE CARE. NOT - * ONLY MUST THE PARAMETER - * STANDARD_MANY - * BE CHANGED TO CORRESPOND TO THE NEW NUMBER OF STANDARDS, BUT ALSO THE - * NUMBERING MUST REMAIN AN UNBROKEN ASCENDING SEQUENCE: DUMMY STANDARDS - * MAY NEED TO BE ADDED. APPROPRIATE CHANGES WILL ALWAYS BE REQUIRED IN - * ROUTINE fillin_formats() AND POSSIBLY ELSEWHERE. BEWARE. - */ -/*---------------------------------------------------------------------------*/ -#define PAL_BGHIN 0 -#define PAL_Nc 2 -#define SECAM 4 -#define NTSC_N 6 -#define NTSC_N_443 8 -#define NTSC_M 1 -#define NTSC_443 3 -#define NTSC_M_JP 5 -#define PAL_60 7 -#define PAL_M 9 -#define PAL_BGHIN_SLOW 10 -#define PAL_Nc_SLOW 12 -#define SECAM_SLOW 14 -#define NTSC_N_SLOW 16 -#define NTSC_N_443_SLOW 18 -#define NTSC_M_SLOW 11 -#define NTSC_443_SLOW 13 -#define NTSC_M_JP_SLOW 15 -#define PAL_60_SLOW 17 -#define PAL_M_SLOW 19 -#define STANDARD_MANY 20 -/*---------------------------------------------------------------------------*/ -/* - * ENUMS - */ -/*---------------------------------------------------------------------------*/ -enum { - AT_720x576, - AT_704x576, - AT_640x480, - AT_720x480, - AT_360x288, - AT_320x240, - AT_360x240, - RESOLUTION_MANY -}; -enum { - FMT_UYVY, - FMT_YUY2, - FMT_RGB24, - FMT_RGB32, - FMT_BGR24, - FMT_BGR32, - PIXELFORMAT_MANY -}; -enum { - FIELD_NONE, - FIELD_INTERLACED, - INTERLACE_MANY -}; -#define SETTINGS_MANY (STANDARD_MANY * \ - RESOLUTION_MANY * \ - 2 * \ - PIXELFORMAT_MANY * \ - INTERLACE_MANY) -/*---------------------------------------------------------------------------*/ -/* - * STRUCTURE DEFINITIONS - */ -/*---------------------------------------------------------------------------*/ -struct easycap_dongle { - struct easycap *peasycap; - struct mutex mutex_video; - struct mutex mutex_audio; -}; -/*---------------------------------------------------------------------------*/ -struct data_buffer { - struct list_head list_head; - void *pgo; - void *pto; - u16 kount; - u16 input; -}; -/*---------------------------------------------------------------------------*/ -struct data_urb { - struct list_head list_head; - struct urb *purb; - int isbuf; - int length; -}; -/*---------------------------------------------------------------------------*/ -struct easycap_standard { - u16 mask; -struct v4l2_standard v4l2_standard; -}; -struct easycap_format { - u16 mask; - char name[128]; -struct v4l2_format v4l2_format; -}; -struct inputset { - int input; - int input_ok; - int standard_offset; - int standard_offset_ok; - int format_offset; - int format_offset_ok; - int brightness; - int brightness_ok; - int contrast; - int contrast_ok; - int saturation; - int saturation_ok; - int hue; - int hue_ok; -}; -/*---------------------------------------------------------------------------*/ -/* - * easycap.ilk == 0 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=256 - * easycap.ilk == 2 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=9 - * easycap.ilk == 3 => FOUR-CVBS HARDWARE, AUDIO wMaxPacketSize=9 - */ -/*---------------------------------------------------------------------------*/ -struct easycap { - int isdongle; - int minor; - - struct video_device video_device; - struct v4l2_device v4l2_device; - - int status; - unsigned int audio_pages_per_fragment; - unsigned int audio_bytes_per_fragment; - unsigned int audio_buffer_page_many; - -#define UPSAMPLE -#ifdef UPSAMPLE - s16 oldaudio; -#endif /*UPSAMPLE*/ - - int ilk; - bool microphone; - - struct usb_device *pusb_device; - struct usb_interface *pusb_interface; - - struct kref kref; - - int queued[FRAME_BUFFER_MANY]; - int done[FRAME_BUFFER_MANY]; - - wait_queue_head_t wq_video; - wait_queue_head_t wq_audio; - wait_queue_head_t wq_trigger; - - int input; - int polled; - int standard_offset; - int format_offset; - struct inputset inputset[INPUT_MANY]; - - bool ntsc; - int fps; - int usec; - int tolerate; - int skip; - int skipped; - int lost[INPUT_MANY]; - int merit[180]; - - long long int dnbydt; - - int video_interface; - int video_altsetting_on; - int video_altsetting_off; - int video_endpointnumber; - int video_isoc_maxframesize; - int video_isoc_buffer_size; - int video_isoc_framesperdesc; - - int video_isoc_streaming; - int video_isoc_sequence; - int video_idle; - int video_eof; - int video_junk; - - struct data_buffer video_isoc_buffer[VIDEO_ISOC_BUFFER_MANY]; - struct data_buffer field_buffer[FIELD_BUFFER_MANY] - [(FIELD_BUFFER_SIZE/PAGE_SIZE)]; - struct data_buffer frame_buffer[FRAME_BUFFER_MANY] - [(FRAME_BUFFER_SIZE/PAGE_SIZE)]; - - struct list_head urb_video_head; - struct list_head *purb_video_head; - - u8 cache[8]; - u8 *pcache; - int video_mt; - int audio_mt; - long long audio_bytes; - u32 isequence; - - int vma_many; -/*---------------------------------------------------------------------------*/ -/* - * BUFFER INDICATORS - */ -/*---------------------------------------------------------------------------*/ - int field_fill; /* Field buffer being filled by easycap_complete(). */ - /* Bumped only by easycap_complete(). */ - int field_page; /* Page of field buffer page being filled by */ - /* easycap_complete(). */ - int field_read; /* Field buffer to be read by field2frame(). */ - /* Bumped only by easycap_complete(). */ - int frame_fill; /* Frame buffer being filled by field2frame(). */ - /* Bumped only by easycap_dqbuf() when */ - /* field2frame() has created a complete frame. */ - int frame_read; /* Frame buffer offered to user by DQBUF. */ - /* Set only by easycap_dqbuf() to trail frame_fill.*/ - int frame_lock; /* Flag set to 1 by DQBUF and cleared by QBUF */ -/*---------------------------------------------------------------------------*/ -/* - * IMAGE PROPERTIES - */ -/*---------------------------------------------------------------------------*/ - u32 pixelformat; - int width; - int height; - int bytesperpixel; - bool byteswaporder; - bool decimatepixel; - bool offerfields; - int frame_buffer_used; - int frame_buffer_many; - int videofieldamount; - - int brightness; - int contrast; - int saturation; - int hue; - - int allocation_video_urb; - int allocation_video_page; - int allocation_video_struct; - int registered_video; -/*---------------------------------------------------------------------------*/ -/* - * ALSA - */ -/*---------------------------------------------------------------------------*/ - struct snd_pcm_hardware alsa_hardware; - struct snd_card *psnd_card; - struct snd_pcm *psnd_pcm; - struct snd_pcm_substream *psubstream; - int dma_fill; - int dma_next; - int dma_read; -/*---------------------------------------------------------------------------*/ -/* - * SOUND PROPERTIES - */ -/*---------------------------------------------------------------------------*/ - int audio_interface; - int audio_altsetting_on; - int audio_altsetting_off; - int audio_endpointnumber; - int audio_isoc_maxframesize; - int audio_isoc_buffer_size; - int audio_isoc_framesperdesc; - - int audio_isoc_streaming; - int audio_idle; - int audio_eof; - int volume; - int mute; - s8 gain; - - struct data_buffer audio_isoc_buffer[AUDIO_ISOC_BUFFER_MANY]; - - struct list_head urb_audio_head; - struct list_head *purb_audio_head; -/*---------------------------------------------------------------------------*/ -/* - * BUFFER INDICATORS - */ -/*---------------------------------------------------------------------------*/ - int audio_fill; /* Audio buffer being filled by easycap_complete(). */ - /* Bumped only by easycap_complete(). */ - int audio_read; /* Audio buffer page being read by easycap_read(). */ - /* Set by easycap_read() to trail audio_fill by */ - /* one fragment. */ -/*---------------------------------------------------------------------------*/ -/* - * SOUND PROPERTIES - */ -/*---------------------------------------------------------------------------*/ - - int audio_buffer_many; - - int allocation_audio_urb; - int allocation_audio_page; - int allocation_audio_struct; - int registered_audio; - - long long int audio_sample; - long long int audio_niveau; - long long int audio_square; - - struct data_buffer audio_buffer[]; -}; -/*---------------------------------------------------------------------------*/ -/* - * VIDEO FUNCTION PROTOTYPES - */ -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -long easycap_unlocked_ioctl(struct file *, unsigned int, unsigned long); -int easycap_dqbuf(struct easycap *, int); -int submit_video_urbs(struct easycap *); -int kill_video_urbs(struct easycap *); -int field2frame(struct easycap *); -int redaub(struct easycap *, void *, void *, - int, int, u8, u8, bool); -void easycap_testcard(struct easycap *, int); -int fillin_formats(void); -int newinput(struct easycap *, int); -int adjust_standard(struct easycap *, v4l2_std_id); -int adjust_format(struct easycap *, u32, u32, u32, - int, bool); -int adjust_brightness(struct easycap *, int); -int adjust_contrast(struct easycap *, int); -int adjust_saturation(struct easycap *, int); -int adjust_hue(struct easycap *, int); -int adjust_volume(struct easycap *, int); -/*---------------------------------------------------------------------------*/ -/* - * AUDIO FUNCTION PROTOTYPES - */ -/*---------------------------------------------------------------------------*/ -int easycap_alsa_probe(struct easycap *); -void easycap_alsa_complete(struct urb *); - -int easycap_sound_setup(struct easycap *); -int submit_audio_urbs(struct easycap *); -int kill_audio_urbs(struct easycap *); -void easyoss_testtone(struct easycap *, int); -int audio_setup(struct easycap *); -/*---------------------------------------------------------------------------*/ -/* - * LOW-LEVEL FUNCTION PROTOTYPES - */ -/*---------------------------------------------------------------------------*/ -int audio_gainget(struct usb_device *); -int audio_gainset(struct usb_device *, s8); - -int set_interface(struct usb_device *, u16); -int wakeup_device(struct usb_device *); -int confirm_resolution(struct usb_device *); -int confirm_stream(struct usb_device *); - -int setup_stk(struct usb_device *, bool); -int setup_saa(struct usb_device *, bool); -int setup_vt(struct usb_device *); -int check_stk(struct usb_device *, bool); -int check_saa(struct usb_device *, bool); -int ready_saa(struct usb_device *); -int merit_saa(struct usb_device *); -int check_vt(struct usb_device *); -int select_input(struct usb_device *, int, int); -int set_resolution(struct usb_device *, - u16, u16, u16, u16); - -int read_saa(struct usb_device *, u16); -int read_stk(struct usb_device *, u32); -int write_saa(struct usb_device *, u16, u16); -int write_000(struct usb_device *, u16, u16); -int start_100(struct usb_device *); -int stop_100(struct usb_device *); -int write_300(struct usb_device *); -int read_vt(struct usb_device *, u16); -int write_vt(struct usb_device *, u16, u16); -int isdongle(struct easycap *); -/*---------------------------------------------------------------------------*/ - - -/*---------------------------------------------------------------------------*/ -/* - * MACROS SAM(...) AND JOM(...) ALLOW DIAGNOSTIC OUTPUT TO BE TAGGED WITH - * THE IDENTITY OF THE DONGLE TO WHICH IT APPLIES, BUT IF INVOKED WHEN THE - * POINTER peasycap IS INVALID AN Oops IS LIKELY, AND ITS CAUSE MAY NOT BE - * IMMEDIATELY OBVIOUS FROM A CASUAL READING OF THE SOURCE CODE. BEWARE. -*/ -/*---------------------------------------------------------------------------*/ -const char *strerror(int err); - -#define SAY(format, args...) do { \ - printk(KERN_DEBUG "easycap:: %s: " \ - format, __func__, ##args); \ -} while (0) -#define SAM(format, args...) do { \ - printk(KERN_DEBUG "easycap::%i%s: " \ - format, peasycap->isdongle, __func__, ##args);\ -} while (0) - -#ifdef CONFIG_EASYCAP_DEBUG -extern int easycap_debug; -#define JOT(n, format, args...) do { \ - if (n <= easycap_debug) { \ - printk(KERN_DEBUG "easycap:: %s: " \ - format, __func__, ##args);\ - } \ -} while (0) -#define JOM(n, format, args...) do { \ - if (n <= easycap_debug) { \ - printk(KERN_DEBUG "easycap::%i%s: " \ - format, peasycap->isdongle, __func__, ##args);\ - } \ -} while (0) - -#else -#define JOT(n, format, args...) do {} while (0) -#define JOM(n, format, args...) do {} while (0) -#endif /* CONFIG_EASYCAP_DEBUG */ - -/*---------------------------------------------------------------------------*/ - -/*---------------------------------------------------------------------------*/ -/* globals - */ -/*---------------------------------------------------------------------------*/ - -extern bool easycap_readback; -extern const struct easycap_standard easycap_standard[]; -extern struct easycap_format easycap_format[]; -extern struct v4l2_queryctrl easycap_control[]; -extern struct usb_driver easycap_usb_driver; -extern struct easycap_dongle easycapdc60_dongle[]; - -#endif /* !__EASYCAP_H__ */ diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/easycap/easycap_ioctl.c deleted file mode 100644 index c99addfb6242..000000000000 --- a/drivers/staging/easycap/easycap_ioctl.c +++ /dev/null @@ -1,2450 +0,0 @@ -/****************************************************************************** -* * -* easycap_ioctl.c * -* * -******************************************************************************/ -/* - * - * Copyright (C) 2010 R.M. Thomas - * - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ -/*****************************************************************************/ - -#include -#include "easycap.h" - -/*--------------------------------------------------------------------------*/ -/* - * UNLESS THERE IS A PREMATURE ERROR RETURN THIS ROUTINE UPDATES THE - * FOLLOWING: - * peasycap->standard_offset - * peasycap->inputset[peasycap->input].standard_offset - * peasycap->fps - * peasycap->usec - * peasycap->tolerate - * peasycap->skip - */ -/*---------------------------------------------------------------------------*/ -int adjust_standard(struct easycap *peasycap, v4l2_std_id std_id) -{ - struct easycap_standard const *peasycap_standard; - u16 reg, set; - int ir, rc, need, k; - unsigned int itwas, isnow; - bool resubmit; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - peasycap_standard = &easycap_standard[0]; - while (0xFFFF != peasycap_standard->mask) { - if (std_id == peasycap_standard->v4l2_standard.id) - break; - peasycap_standard++; - } - if (0xFFFF == peasycap_standard->mask) { - peasycap_standard = &easycap_standard[0]; - while (0xFFFF != peasycap_standard->mask) { - if (std_id & peasycap_standard->v4l2_standard.id) - break; - peasycap_standard++; - } - } - if (0xFFFF == peasycap_standard->mask) { - SAM("ERROR: 0x%08X=std_id: standard not found\n", - (unsigned int)std_id); - return -EINVAL; - } - SAM("selected standard: %s\n", - &(peasycap_standard->v4l2_standard.name[0])); - if (peasycap->standard_offset == peasycap_standard - easycap_standard) { - SAM("requested standard already in effect\n"); - return 0; - } - peasycap->standard_offset = peasycap_standard - easycap_standard; - for (k = 0; k < INPUT_MANY; k++) { - if (!peasycap->inputset[k].standard_offset_ok) { - peasycap->inputset[k].standard_offset = - peasycap->standard_offset; - } - } - if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { - peasycap->inputset[peasycap->input].standard_offset = - peasycap->standard_offset; - peasycap->inputset[peasycap->input].standard_offset_ok = 1; - } else - JOM(8, "%i=peasycap->input\n", peasycap->input); - - peasycap->fps = peasycap_standard->v4l2_standard.frameperiod.denominator / - peasycap_standard->v4l2_standard.frameperiod.numerator; - switch (peasycap->fps) { - case 6: - case 30: { - peasycap->ntsc = true; - break; - } - case 5: - case 25: { - peasycap->ntsc = false; - break; - } - default: { - SAM("MISTAKE: %i=frames-per-second\n", peasycap->fps); - return -ENOENT; - } - } - JOM(8, "%i frames-per-second\n", peasycap->fps); - if (0x8000 & peasycap_standard->mask) { - peasycap->skip = 5; - peasycap->usec = 1000000 / (2 * (5 * peasycap->fps)); - peasycap->tolerate = 1000 * (25 / (5 * peasycap->fps)); - } else { - peasycap->skip = 0; - peasycap->usec = 1000000 / (2 * peasycap->fps); - peasycap->tolerate = 1000 * (25 / peasycap->fps); - } - if (peasycap->video_isoc_streaming) { - resubmit = true; - kill_video_urbs(peasycap); - } else - resubmit = false; -/*--------------------------------------------------------------------------*/ -/* - * SAA7113H DATASHEET PAGE 44, TABLE 42 - */ -/*--------------------------------------------------------------------------*/ - need = 0; - itwas = 0; - reg = 0x00; - set = 0x00; - switch (peasycap_standard->mask & 0x000F) { - case NTSC_M_JP: { - reg = 0x0A; - set = 0x95; - ir = read_saa(peasycap->pusb_device, reg); - if (0 > ir) - SAM("ERROR: cannot read SAA register 0x%02X\n", reg); - else - itwas = (unsigned int)ir; - rc = write_saa(peasycap->pusb_device, reg, set); - if (rc) - SAM("ERROR: failed to set SAA register " - "0x%02X to 0x%02X for JP standard\n", reg, set); - else { - isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); - if (0 > ir) - JOM(8, "SAA register 0x%02X changed " - "to 0x%02X\n", reg, isnow); - else - JOM(8, "SAA register 0x%02X changed " - "from 0x%02X to 0x%02X\n", reg, itwas, isnow); - } - - reg = 0x0B; - set = 0x48; - ir = read_saa(peasycap->pusb_device, reg); - if (0 > ir) - SAM("ERROR: cannot read SAA register 0x%02X\n", reg); - else - itwas = (unsigned int)ir; - rc = write_saa(peasycap->pusb_device, reg, set); - if (rc) - SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X " - "for JP standard\n", reg, set); - else { - isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); - if (0 > ir) - JOM(8, "SAA register 0x%02X changed " - "to 0x%02X\n", reg, isnow); - else - JOM(8, "SAA register 0x%02X changed " - "from 0x%02X to 0x%02X\n", reg, itwas, isnow); - } -/*--------------------------------------------------------------------------*/ -/* - * NOTE: NO break HERE: RUN ON TO NEXT CASE - */ -/*--------------------------------------------------------------------------*/ - } - case NTSC_M: - case PAL_BGHIN: { - reg = 0x0E; - set = 0x01; - need = 1; - break; - } - case NTSC_N_443: - case PAL_60: { - reg = 0x0E; - set = 0x11; - need = 1; - break; - } - case NTSC_443: - case PAL_Nc: { - reg = 0x0E; - set = 0x21; - need = 1; - break; - } - case NTSC_N: - case PAL_M: { - reg = 0x0E; - set = 0x31; - need = 1; - break; - } - case SECAM: { - reg = 0x0E; - set = 0x51; - need = 1; - break; - } - default: - break; - } -/*--------------------------------------------------------------------------*/ - if (need) { - ir = read_saa(peasycap->pusb_device, reg); - if (0 > ir) - SAM("ERROR: failed to read SAA register 0x%02X\n", reg); - else - itwas = (unsigned int)ir; - rc = write_saa(peasycap->pusb_device, reg, set); - if (0 != write_saa(peasycap->pusb_device, reg, set)) { - SAM("ERROR: failed to set SAA register " - "0x%02X to 0x%02X for table 42\n", reg, set); - } else { - isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); - if (0 > ir) - JOM(8, "SAA register 0x%02X changed " - "to 0x%02X\n", reg, isnow); - else - JOM(8, "SAA register 0x%02X changed " - "from 0x%02X to 0x%02X\n", reg, itwas, isnow); - } - } -/*--------------------------------------------------------------------------*/ -/* - * SAA7113H DATASHEET PAGE 41 - */ -/*--------------------------------------------------------------------------*/ - reg = 0x08; - ir = read_saa(peasycap->pusb_device, reg); - if (0 > ir) - SAM("ERROR: failed to read SAA register 0x%02X " - "so cannot reset\n", reg); - else { - itwas = (unsigned int)ir; - if (peasycap_standard->mask & 0x0001) - set = itwas | 0x40 ; - else - set = itwas & ~0x40 ; - rc = write_saa(peasycap->pusb_device, reg, set); - if (rc) - SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n", - reg, set); - else { - isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); - if (0 > ir) - JOM(8, "SAA register 0x%02X changed to 0x%02X\n", - reg, isnow); - else - JOM(8, "SAA register 0x%02X changed " - "from 0x%02X to 0x%02X\n", reg, itwas, isnow); - } - } -/*--------------------------------------------------------------------------*/ -/* - * SAA7113H DATASHEET PAGE 51, TABLE 57 - */ -/*---------------------------------------------------------------------------*/ - reg = 0x40; - ir = read_saa(peasycap->pusb_device, reg); - if (0 > ir) - SAM("ERROR: failed to read SAA register 0x%02X " - "so cannot reset\n", reg); - else { - itwas = (unsigned int)ir; - if (peasycap_standard->mask & 0x0001) - set = itwas | 0x80 ; - else - set = itwas & ~0x80 ; - rc = write_saa(peasycap->pusb_device, reg, set); - if (rc) - SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n", - reg, set); - else { - isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); - if (0 > ir) - JOM(8, "SAA register 0x%02X changed to 0x%02X\n", - reg, isnow); - else - JOM(8, "SAA register 0x%02X changed " - "from 0x%02X to 0x%02X\n", reg, itwas, isnow); - } - } -/*--------------------------------------------------------------------------*/ -/* - * SAA7113H DATASHEET PAGE 53, TABLE 66 - */ -/*--------------------------------------------------------------------------*/ - reg = 0x5A; - ir = read_saa(peasycap->pusb_device, reg); - if (0 > ir) - SAM("ERROR: failed to read SAA register 0x%02X but continuing\n", reg); - itwas = (unsigned int)ir; - if (peasycap_standard->mask & 0x0001) - set = 0x0A ; - else - set = 0x07 ; - if (0 != write_saa(peasycap->pusb_device, reg, set)) - SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n", - reg, set); - else { - isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); - if (0 > ir) - JOM(8, "SAA register 0x%02X changed " - "to 0x%02X\n", reg, isnow); - else - JOM(8, "SAA register 0x%02X changed " - "from 0x%02X to 0x%02X\n", reg, itwas, isnow); - } - if (resubmit) - submit_video_urbs(peasycap); - return 0; -} -/*****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * THE ALGORITHM FOR RESPONDING TO THE VIDIO_S_FMT IOCTL REQUIRES - * A VALID VALUE OF peasycap->standard_offset, OTHERWISE -EBUSY IS RETURNED. - * - * PROVIDED THE ARGUMENT try IS false AND THERE IS NO PREMATURE ERROR RETURN - * THIS ROUTINE UPDATES THE FOLLOWING: - * peasycap->format_offset - * peasycap->inputset[peasycap->input].format_offset - * peasycap->pixelformat - * peasycap->height - * peasycap->width - * peasycap->bytesperpixel - * peasycap->byteswaporder - * peasycap->decimatepixel - * peasycap->frame_buffer_used - * peasycap->videofieldamount - * peasycap->offerfields - * - * IF SUCCESSFUL THE FUNCTION RETURNS THE OFFSET IN easycap_format[] - * IDENTIFYING THE FORMAT WHICH IS TO RETURNED TO THE USER. - * ERRORS RETURN A NEGATIVE NUMBER. - */ -/*--------------------------------------------------------------------------*/ -int adjust_format(struct easycap *peasycap, - u32 width, u32 height, u32 pixelformat, int field, bool try) -{ - struct easycap_format *peasycap_format, *peasycap_best_format; - u16 mask; - struct usb_device *p; - int miss, multiplier, best, k; - char bf[5], fo[32], *pc; - u32 uc; - bool resubmit; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (0 > peasycap->standard_offset) { - JOM(8, "%i=peasycap->standard_offset\n", peasycap->standard_offset); - return -EBUSY; - } - p = peasycap->pusb_device; - if (!p) { - SAM("ERROR: peaycap->pusb_device is NULL\n"); - return -EFAULT; - } - pc = &bf[0]; - uc = pixelformat; - memcpy((void *)pc, (void *)(&uc), 4); - bf[4] = 0; - mask = 0xFF & easycap_standard[peasycap->standard_offset].mask; - SAM("sought: %ix%i,%s(0x%08X),%i=field,0x%02X=std mask\n", - width, height, pc, pixelformat, field, mask); - switch (field) { - case V4L2_FIELD_ANY: { - strcpy(&fo[0], "V4L2_FIELD_ANY "); - break; - } - case V4L2_FIELD_NONE: { - strcpy(&fo[0], "V4L2_FIELD_NONE"); - break; - } - case V4L2_FIELD_TOP: { - strcpy(&fo[0], "V4L2_FIELD_TOP"); - break; - } - case V4L2_FIELD_BOTTOM: { - strcpy(&fo[0], "V4L2_FIELD_BOTTOM"); - break; - } - case V4L2_FIELD_INTERLACED: { - strcpy(&fo[0], "V4L2_FIELD_INTERLACED"); - break; - } - case V4L2_FIELD_SEQ_TB: { - strcpy(&fo[0], "V4L2_FIELD_SEQ_TB"); - break; - } - case V4L2_FIELD_SEQ_BT: { - strcpy(&fo[0], "V4L2_FIELD_SEQ_BT"); - break; - } - case V4L2_FIELD_ALTERNATE: { - strcpy(&fo[0], "V4L2_FIELD_ALTERNATE"); - break; - } - case V4L2_FIELD_INTERLACED_TB: { - strcpy(&fo[0], "V4L2_FIELD_INTERLACED_TB"); - break; - } - case V4L2_FIELD_INTERLACED_BT: { - strcpy(&fo[0], "V4L2_FIELD_INTERLACED_BT"); - break; - } - default: { - strcpy(&fo[0], "V4L2_FIELD_... UNKNOWN "); - break; - } - } - SAM("sought: %s\n", &fo[0]); - if (V4L2_FIELD_ANY == field) { - field = V4L2_FIELD_NONE; - SAM("prefer: V4L2_FIELD_NONE=field, was V4L2_FIELD_ANY\n"); - } - peasycap_best_format = NULL; - peasycap_format = &easycap_format[0]; - while (0 != peasycap_format->v4l2_format.fmt.pix.width) { - JOM(16, ".> %i %i 0x%08X %ix%i\n", - peasycap_format->mask & 0x01, - peasycap_format->v4l2_format.fmt.pix.field, - peasycap_format->v4l2_format.fmt.pix.pixelformat, - peasycap_format->v4l2_format.fmt.pix.width, - peasycap_format->v4l2_format.fmt.pix.height); - - if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) && - (peasycap_format->v4l2_format.fmt.pix.field == field) && - (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat) && - (peasycap_format->v4l2_format.fmt.pix.width == width) && - (peasycap_format->v4l2_format.fmt.pix.height == height)) { - - peasycap_best_format = peasycap_format; - break; - } - peasycap_format++; - } - if (0 == peasycap_format->v4l2_format.fmt.pix.width) { - SAM("cannot do: %ix%i with standard mask 0x%02X\n", - width, height, mask); - peasycap_format = &easycap_format[0]; - best = -1; - while (0 != peasycap_format->v4l2_format.fmt.pix.width) { - if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) && - (peasycap_format->v4l2_format.fmt.pix.field == field) && - (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat)) { - - miss = abs(peasycap_format->v4l2_format.fmt.pix.width - width); - if ((best > miss) || (best < 0)) { - best = miss; - peasycap_best_format = peasycap_format; - if (!miss) - break; - } - } - peasycap_format++; - } - if (-1 == best) { - SAM("cannot do %ix... with standard mask 0x%02X\n", - width, mask); - SAM("cannot do ...x%i with standard mask 0x%02X\n", - height, mask); - SAM(" %ix%i unmatched\n", width, height); - return peasycap->format_offset; - } - } - if (!peasycap_best_format) { - SAM("MISTAKE: peasycap_best_format is NULL"); - return -EINVAL; - } - peasycap_format = peasycap_best_format; - -/*...........................................................................*/ - if (try) - return peasycap_best_format - easycap_format; -/*...........................................................................*/ - - if (false != try) { - SAM("MISTAKE: true==try where is should be false\n"); - return -EINVAL; - } - SAM("actioning: %ix%i %s\n", - peasycap_format->v4l2_format.fmt.pix.width, - peasycap_format->v4l2_format.fmt.pix.height, - &peasycap_format->name[0]); - peasycap->height = peasycap_format->v4l2_format.fmt.pix.height; - peasycap->width = peasycap_format->v4l2_format.fmt.pix.width; - peasycap->pixelformat = peasycap_format->v4l2_format.fmt.pix.pixelformat; - peasycap->format_offset = peasycap_format - easycap_format; - - - for (k = 0; k < INPUT_MANY; k++) { - if (!peasycap->inputset[k].format_offset_ok) { - peasycap->inputset[k].format_offset = - peasycap->format_offset; - } - } - if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { - peasycap->inputset[peasycap->input].format_offset = - peasycap->format_offset; - peasycap->inputset[peasycap->input].format_offset_ok = 1; - } else - JOM(8, "%i=peasycap->input\n", peasycap->input); - - - - peasycap->bytesperpixel = (0x00E0 & peasycap_format->mask) >> 5 ; - if (0x0100 & peasycap_format->mask) - peasycap->byteswaporder = true; - else - peasycap->byteswaporder = false; - if (0x0200 & peasycap_format->mask) - peasycap->skip = 5; - else - peasycap->skip = 0; - if (0x0800 & peasycap_format->mask) - peasycap->decimatepixel = true; - else - peasycap->decimatepixel = false; - if (0x1000 & peasycap_format->mask) - peasycap->offerfields = true; - else - peasycap->offerfields = false; - if (peasycap->decimatepixel) - multiplier = 2; - else - multiplier = 1; - peasycap->videofieldamount = - multiplier * peasycap->width * multiplier * peasycap->height; - peasycap->frame_buffer_used = - peasycap->bytesperpixel * peasycap->width * peasycap->height; - if (peasycap->video_isoc_streaming) { - resubmit = true; - kill_video_urbs(peasycap); - } else - resubmit = false; -/*---------------------------------------------------------------------------*/ -/* - * PAL - */ -/*---------------------------------------------------------------------------*/ - if (0 == (0x01 & peasycap_format->mask)) { - if (((720 == peasycap_format->v4l2_format.fmt.pix.width) && - (576 == peasycap_format->v4l2_format.fmt.pix.height)) || - ((360 == peasycap_format->v4l2_format.fmt.pix.width) && - (288 == peasycap_format->v4l2_format.fmt.pix.height))) { - if (set_resolution(p, 0x0000, 0x0001, 0x05A0, 0x0121)) { - SAM("ERROR: set_resolution() failed\n"); - return -EINVAL; - } - } else if ((704 == peasycap_format->v4l2_format.fmt.pix.width) && - (576 == peasycap_format->v4l2_format.fmt.pix.height)) { - if (set_resolution(p, 0x0004, 0x0001, 0x0584, 0x0121)) { - SAM("ERROR: set_resolution() failed\n"); - return -EINVAL; - } - } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) && - (480 == peasycap_format->v4l2_format.fmt.pix.height)) || - ((320 == peasycap_format->v4l2_format.fmt.pix.width) && - (240 == peasycap_format->v4l2_format.fmt.pix.height))) { - if (set_resolution(p, 0x0014, 0x0020, 0x0514, 0x0110)) { - SAM("ERROR: set_resolution() failed\n"); - return -EINVAL; - } - } else { - SAM("MISTAKE: bad format, cannot set resolution\n"); - return -EINVAL; - } -/*---------------------------------------------------------------------------*/ -/* - * NTSC - */ -/*---------------------------------------------------------------------------*/ - } else { - if (((720 == peasycap_format->v4l2_format.fmt.pix.width) && - (480 == peasycap_format->v4l2_format.fmt.pix.height)) || - ((360 == peasycap_format->v4l2_format.fmt.pix.width) && - (240 == peasycap_format->v4l2_format.fmt.pix.height))) { - if (set_resolution(p, 0x0000, 0x0003, 0x05A0, 0x00F3)) { - SAM("ERROR: set_resolution() failed\n"); - return -EINVAL; - } - } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) && - (480 == peasycap_format->v4l2_format.fmt.pix.height)) || - ((320 == peasycap_format->v4l2_format.fmt.pix.width) && - (240 == peasycap_format->v4l2_format.fmt.pix.height))) { - if (set_resolution(p, 0x0014, 0x0003, 0x0514, 0x00F3)) { - SAM("ERROR: set_resolution() failed\n"); - return -EINVAL; - } - } else { - SAM("MISTAKE: bad format, cannot set resolution\n"); - return -EINVAL; - } - } -/*---------------------------------------------------------------------------*/ - if (resubmit) - submit_video_urbs(peasycap); - - return peasycap_best_format - easycap_format; -} -/*****************************************************************************/ -int adjust_brightness(struct easycap *peasycap, int value) -{ - unsigned int mood; - int i1, k; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - i1 = 0; - while (0xFFFFFFFF != easycap_control[i1].id) { - if (V4L2_CID_BRIGHTNESS == easycap_control[i1].id) { - if ((easycap_control[i1].minimum > value) || - (easycap_control[i1].maximum < value)) - value = easycap_control[i1].default_value; - - if ((easycap_control[i1].minimum <= peasycap->brightness) && - (easycap_control[i1].maximum >= peasycap->brightness)) { - if (peasycap->brightness == value) { - SAM("unchanged brightness at 0x%02X\n", - value); - return 0; - } - } - peasycap->brightness = value; - for (k = 0; k < INPUT_MANY; k++) { - if (!peasycap->inputset[k].brightness_ok) - peasycap->inputset[k].brightness = - peasycap->brightness; - } - if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { - peasycap->inputset[peasycap->input].brightness = - peasycap->brightness; - peasycap->inputset[peasycap->input].brightness_ok = 1; - } else - JOM(8, "%i=peasycap->input\n", peasycap->input); - mood = 0x00FF & (unsigned int)peasycap->brightness; - if (!write_saa(peasycap->pusb_device, 0x0A, mood)) { - SAM("adjusting brightness to 0x%02X\n", mood); - return 0; - } else { - SAM("WARNING: failed to adjust brightness " - "to 0x%02X\n", mood); - return -ENOENT; - } - break; - } - i1++; - } - SAM("WARNING: failed to adjust brightness: control not found\n"); - return -ENOENT; -} -/*****************************************************************************/ -int adjust_contrast(struct easycap *peasycap, int value) -{ - unsigned int mood; - int i1, k; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - i1 = 0; - while (0xFFFFFFFF != easycap_control[i1].id) { - if (V4L2_CID_CONTRAST == easycap_control[i1].id) { - if ((easycap_control[i1].minimum > value) || - (easycap_control[i1].maximum < value)) - value = easycap_control[i1].default_value; - - - if ((easycap_control[i1].minimum <= peasycap->contrast) && - (easycap_control[i1].maximum >= peasycap->contrast)) { - if (peasycap->contrast == value) { - SAM("unchanged contrast at 0x%02X\n", value); - return 0; - } - } - peasycap->contrast = value; - for (k = 0; k < INPUT_MANY; k++) { - if (!peasycap->inputset[k].contrast_ok) - peasycap->inputset[k].contrast = peasycap->contrast; - } - - if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { - peasycap->inputset[peasycap->input].contrast = - peasycap->contrast; - peasycap->inputset[peasycap->input].contrast_ok = 1; - } else - JOM(8, "%i=peasycap->input\n", peasycap->input); - - mood = 0x00FF & (unsigned int) (peasycap->contrast - 128); - if (!write_saa(peasycap->pusb_device, 0x0B, mood)) { - SAM("adjusting contrast to 0x%02X\n", mood); - return 0; - } else { - SAM("WARNING: failed to adjust contrast to " - "0x%02X\n", mood); - return -ENOENT; - } - break; - } - i1++; - } - SAM("WARNING: failed to adjust contrast: control not found\n"); - return -ENOENT; -} -/*****************************************************************************/ -int adjust_saturation(struct easycap *peasycap, int value) -{ - unsigned int mood; - int i1, k; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - i1 = 0; - while (0xFFFFFFFF != easycap_control[i1].id) { - if (V4L2_CID_SATURATION == easycap_control[i1].id) { - if ((easycap_control[i1].minimum > value) || - (easycap_control[i1].maximum < value)) - value = easycap_control[i1].default_value; - - - if ((easycap_control[i1].minimum <= peasycap->saturation) && - (easycap_control[i1].maximum >= peasycap->saturation)) { - if (peasycap->saturation == value) { - SAM("unchanged saturation at 0x%02X\n", - value); - return 0; - } - } - peasycap->saturation = value; - for (k = 0; k < INPUT_MANY; k++) { - if (!peasycap->inputset[k].saturation_ok) - peasycap->inputset[k].saturation = - peasycap->saturation; - } - if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { - peasycap->inputset[peasycap->input].saturation = - peasycap->saturation; - peasycap->inputset[peasycap->input].saturation_ok = 1; - } else - JOM(8, "%i=peasycap->input\n", peasycap->input); - mood = 0x00FF & (unsigned int) (peasycap->saturation - 128); - if (!write_saa(peasycap->pusb_device, 0x0C, mood)) { - SAM("adjusting saturation to 0x%02X\n", mood); - return 0; - } else { - SAM("WARNING: failed to adjust saturation to " - "0x%02X\n", mood); - return -ENOENT; - } - break; - } - i1++; - } - SAM("WARNING: failed to adjust saturation: control not found\n"); - return -ENOENT; -} -/*****************************************************************************/ -int adjust_hue(struct easycap *peasycap, int value) -{ - unsigned int mood; - int i1, i2, k; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - i1 = 0; - while (0xFFFFFFFF != easycap_control[i1].id) { - if (V4L2_CID_HUE == easycap_control[i1].id) { - if ((easycap_control[i1].minimum > value) || - (easycap_control[i1].maximum < value)) - value = easycap_control[i1].default_value; - - if ((easycap_control[i1].minimum <= peasycap->hue) && - (easycap_control[i1].maximum >= peasycap->hue)) { - if (peasycap->hue == value) { - SAM("unchanged hue at 0x%02X\n", value); - return 0; - } - } - peasycap->hue = value; - for (k = 0; k < INPUT_MANY; k++) { - if (!peasycap->inputset[k].hue_ok) - peasycap->inputset[k].hue = peasycap->hue; - } - if (0 <= peasycap->input && INPUT_MANY > peasycap->input) { - peasycap->inputset[peasycap->input].hue = peasycap->hue; - peasycap->inputset[peasycap->input].hue_ok = 1; - } else - JOM(8, "%i=peasycap->input\n", peasycap->input); - i2 = peasycap->hue - 128; - mood = 0x00FF & ((int) i2); - if (!write_saa(peasycap->pusb_device, 0x0D, mood)) { - SAM("adjusting hue to 0x%02X\n", mood); - return 0; - } else { - SAM("WARNING: failed to adjust hue to 0x%02X\n", mood); - return -ENOENT; - } - break; - } - i1++; - } - SAM("WARNING: failed to adjust hue: control not found\n"); - return -ENOENT; -} -/*****************************************************************************/ -int adjust_volume(struct easycap *peasycap, int value) -{ - s8 mood; - int i1; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - i1 = 0; - while (0xFFFFFFFF != easycap_control[i1].id) { - if (V4L2_CID_AUDIO_VOLUME == easycap_control[i1].id) { - if ((easycap_control[i1].minimum > value) || - (easycap_control[i1].maximum < value)) - value = easycap_control[i1].default_value; - - if ((easycap_control[i1].minimum <= peasycap->volume) && - (easycap_control[i1].maximum >= peasycap->volume)) { - if (peasycap->volume == value) { - SAM("unchanged volume at 0x%02X\n", value); - return 0; - } - } - peasycap->volume = value; - mood = (16 > peasycap->volume) ? 16 : - ((31 < peasycap->volume) ? 31 : - (s8) peasycap->volume); - if (!audio_gainset(peasycap->pusb_device, mood)) { - SAM("adjusting volume to 0x%02X\n", mood); - return 0; - } else { - SAM("WARNING: failed to adjust volume to " - "0x%2X\n", mood); - return -ENOENT; - } - break; - } - i1++; - } - SAM("WARNING: failed to adjust volume: control not found\n"); - return -ENOENT; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * AN ALTERNATIVE METHOD OF MUTING MIGHT SEEM TO BE: - * usb_set_interface(peasycap->pusb_device, - * peasycap->audio_interface, - * peasycap->audio_altsetting_off); - * HOWEVER, AFTER THIS COMMAND IS ISSUED ALL SUBSEQUENT URBS RECEIVE STATUS - * -ESHUTDOWN. THE HANDLER ROUTINE easyxxx_complete() DECLINES TO RESUBMIT - * THE URB AND THE PIPELINE COLLAPSES IRRETRIEVABLY. BEWARE. - */ -/*---------------------------------------------------------------------------*/ -static int adjust_mute(struct easycap *peasycap, int value) -{ - int i1; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - i1 = 0; - while (0xFFFFFFFF != easycap_control[i1].id) { - if (V4L2_CID_AUDIO_MUTE == easycap_control[i1].id) { - peasycap->mute = value; - switch (peasycap->mute) { - case 1: { - peasycap->audio_idle = 1; - SAM("adjusting mute: %i=peasycap->audio_idle\n", - peasycap->audio_idle); - return 0; - } - default: { - peasycap->audio_idle = 0; - SAM("adjusting mute: %i=peasycap->audio_idle\n", - peasycap->audio_idle); - return 0; - } - } - break; - } - i1++; - } - SAM("WARNING: failed to adjust mute: control not found\n"); - return -ENOENT; -} -/*---------------------------------------------------------------------------*/ -long easycap_unlocked_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct easycap *peasycap; - struct usb_device *p; - int kd; - - if (!file) { - SAY("ERROR: file is NULL\n"); - return -ERESTARTSYS; - } - peasycap = file->private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -1; - } - p = peasycap->pusb_device; - if (!p) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - kd = isdongle(peasycap); - if (0 <= kd && DONGLE_MANY > kd) { - if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { - SAY("ERROR: cannot lock " - "easycapdc60_dongle[%i].mutex_video\n", kd); - return -ERESTARTSYS; - } - JOM(4, "locked easycapdc60_dongle[%i].mutex_video\n", kd); -/*---------------------------------------------------------------------------*/ -/* - * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap, - * IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL. - * IF NECESSARY, BAIL OUT. - */ -/*---------------------------------------------------------------------------*/ - if (kd != isdongle(peasycap)) - return -ERESTARTSYS; - if (!file) { - SAY("ERROR: file is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ERESTARTSYS; - } - peasycap = file->private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ERESTARTSYS; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ERESTARTSYS; - } - } else { -/*---------------------------------------------------------------------------*/ -/* - * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE - * ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT. - */ -/*---------------------------------------------------------------------------*/ - return -ERESTARTSYS; - } -/*---------------------------------------------------------------------------*/ - switch (cmd) { - case VIDIOC_QUERYCAP: { - struct v4l2_capability v4l2_capability; - char version[16], *p1, *p2; - int i, rc, k[3]; - long lng; - - JOM(8, "VIDIOC_QUERYCAP\n"); - - if (16 <= strlen(EASYCAP_DRIVER_VERSION)) { - SAM("ERROR: bad driver version string\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - strcpy(&version[0], EASYCAP_DRIVER_VERSION); - for (i = 0; i < 3; i++) - k[i] = 0; - p2 = &version[0]; - i = 0; - while (*p2) { - p1 = p2; - while (*p2 && ('.' != *p2)) - p2++; - if (*p2) - *p2++ = 0; - if (3 > i) { - rc = (int) strict_strtol(p1, 10, &lng); - if (rc) { - SAM("ERROR: %i=strict_strtol(%s,.,,)\n", - rc, p1); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - k[i] = (int)lng; - } - i++; - } - - memset(&v4l2_capability, 0, sizeof(struct v4l2_capability)); - strlcpy(&v4l2_capability.driver[0], - "easycap", sizeof(v4l2_capability.driver)); - - v4l2_capability.capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING | - V4L2_CAP_AUDIO | - V4L2_CAP_READWRITE; - - v4l2_capability.version = KERNEL_VERSION(k[0], k[1], k[2]); - JOM(8, "v4l2_capability.version=(%i,%i,%i)\n", k[0], k[1], k[2]); - - strlcpy(&v4l2_capability.card[0], - "EasyCAP DC60", sizeof(v4l2_capability.card)); - - if (usb_make_path(peasycap->pusb_device, - &v4l2_capability.bus_info[0], - sizeof(v4l2_capability.bus_info)) < 0) { - - strlcpy(&v4l2_capability.bus_info[0], "EasyCAP bus_info", - sizeof(v4l2_capability.bus_info)); - JOM(8, "%s=v4l2_capability.bus_info\n", - &v4l2_capability.bus_info[0]); - } - if (copy_to_user((void __user *)arg, &v4l2_capability, - sizeof(struct v4l2_capability))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_ENUMINPUT: { - struct v4l2_input v4l2_input; - u32 index; - - JOM(8, "VIDIOC_ENUMINPUT\n"); - - if (copy_from_user(&v4l2_input, (void __user *)arg, - sizeof(struct v4l2_input))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - index = v4l2_input.index; - memset(&v4l2_input, 0, sizeof(struct v4l2_input)); - - switch (index) { - case 0: { - v4l2_input.index = index; - strcpy(&v4l2_input.name[0], "CVBS0"); - v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; - v4l2_input.audioset = 0x01; - v4l2_input.tuner = 0; - v4l2_input.std = V4L2_STD_PAL | - V4L2_STD_SECAM | - V4L2_STD_NTSC ; - v4l2_input.status = 0; - JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); - break; - } - case 1: { - v4l2_input.index = index; - strcpy(&v4l2_input.name[0], "CVBS1"); - v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; - v4l2_input.audioset = 0x01; - v4l2_input.tuner = 0; - v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | - V4L2_STD_NTSC; - v4l2_input.status = 0; - JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); - break; - } - case 2: { - v4l2_input.index = index; - strcpy(&v4l2_input.name[0], "CVBS2"); - v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; - v4l2_input.audioset = 0x01; - v4l2_input.tuner = 0; - v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | - V4L2_STD_NTSC ; - v4l2_input.status = 0; - JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); - break; - } - case 3: { - v4l2_input.index = index; - strcpy(&v4l2_input.name[0], "CVBS3"); - v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; - v4l2_input.audioset = 0x01; - v4l2_input.tuner = 0; - v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | - V4L2_STD_NTSC ; - v4l2_input.status = 0; - JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); - break; - } - case 4: { - v4l2_input.index = index; - strcpy(&v4l2_input.name[0], "CVBS4"); - v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; - v4l2_input.audioset = 0x01; - v4l2_input.tuner = 0; - v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | - V4L2_STD_NTSC ; - v4l2_input.status = 0; - JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); - break; - } - case 5: { - v4l2_input.index = index; - strcpy(&v4l2_input.name[0], "S-VIDEO"); - v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; - v4l2_input.audioset = 0x01; - v4l2_input.tuner = 0; - v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | - V4L2_STD_NTSC ; - v4l2_input.status = 0; - JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); - break; - } - default: { - JOM(8, "%i=index: exhausts inputs\n", index); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - } - - if (copy_to_user((void __user *)arg, &v4l2_input, - sizeof(struct v4l2_input))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_INPUT: { - u32 index; - - JOM(8, "VIDIOC_G_INPUT\n"); - index = (u32)peasycap->input; - JOM(8, "user is told: %i\n", index); - if (copy_to_user((void __user *)arg, &index, sizeof(u32))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_S_INPUT: - { - u32 index; - int rc; - - JOM(8, "VIDIOC_S_INPUT\n"); - - if (0 != copy_from_user(&index, (void __user *)arg, sizeof(u32))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - JOM(8, "user requests input %i\n", index); - - if ((int)index == peasycap->input) { - SAM("requested input already in effect\n"); - break; - } - - if ((0 > index) || (INPUT_MANY <= index)) { - JOM(8, "ERROR: bad requested input: %i\n", index); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - - rc = newinput(peasycap, (int)index); - if (0 == rc) { - JOM(8, "newinput(.,%i) OK\n", (int)index); - } else { - SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_ENUMAUDIO: { - JOM(8, "VIDIOC_ENUMAUDIO\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_ENUMAUDOUT: { - struct v4l2_audioout v4l2_audioout; - - JOM(8, "VIDIOC_ENUMAUDOUT\n"); - - if (copy_from_user(&v4l2_audioout, (void __user *)arg, - sizeof(struct v4l2_audioout))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - if (0 != v4l2_audioout.index) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout)); - v4l2_audioout.index = 0; - strcpy(&v4l2_audioout.name[0], "Soundtrack"); - - if (copy_to_user((void __user *)arg, &v4l2_audioout, - sizeof(struct v4l2_audioout))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_QUERYCTRL: { - int i1; - struct v4l2_queryctrl v4l2_queryctrl; - - JOM(8, "VIDIOC_QUERYCTRL\n"); - - if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg, - sizeof(struct v4l2_queryctrl))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - i1 = 0; - while (0xFFFFFFFF != easycap_control[i1].id) { - if (easycap_control[i1].id == v4l2_queryctrl.id) { - JOM(8, "VIDIOC_QUERYCTRL %s=easycap_control[%i]" - ".name\n", &easycap_control[i1].name[0], i1); - memcpy(&v4l2_queryctrl, &easycap_control[i1], - sizeof(struct v4l2_queryctrl)); - break; - } - i1++; - } - if (0xFFFFFFFF == easycap_control[i1].id) { - JOM(8, "%i=index: exhausts controls\n", i1); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - if (copy_to_user((void __user *)arg, &v4l2_queryctrl, - sizeof(struct v4l2_queryctrl))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_QUERYMENU: { - JOM(8, "VIDIOC_QUERYMENU unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_CTRL: { - struct v4l2_control *pv4l2_control; - - JOM(8, "VIDIOC_G_CTRL\n"); - pv4l2_control = memdup_user((void __user *)arg, - sizeof(struct v4l2_control)); - if (IS_ERR(pv4l2_control)) { - SAM("ERROR: copy from user failed\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return PTR_ERR(pv4l2_control); - } - - switch (pv4l2_control->id) { - case V4L2_CID_BRIGHTNESS: { - pv4l2_control->value = peasycap->brightness; - JOM(8, "user enquires brightness: %i\n", pv4l2_control->value); - break; - } - case V4L2_CID_CONTRAST: { - pv4l2_control->value = peasycap->contrast; - JOM(8, "user enquires contrast: %i\n", pv4l2_control->value); - break; - } - case V4L2_CID_SATURATION: { - pv4l2_control->value = peasycap->saturation; - JOM(8, "user enquires saturation: %i\n", pv4l2_control->value); - break; - } - case V4L2_CID_HUE: { - pv4l2_control->value = peasycap->hue; - JOM(8, "user enquires hue: %i\n", pv4l2_control->value); - break; - } - case V4L2_CID_AUDIO_VOLUME: { - pv4l2_control->value = peasycap->volume; - JOM(8, "user enquires volume: %i\n", pv4l2_control->value); - break; - } - case V4L2_CID_AUDIO_MUTE: { - if (1 == peasycap->mute) - pv4l2_control->value = true; - else - pv4l2_control->value = false; - JOM(8, "user enquires mute: %i\n", pv4l2_control->value); - break; - } - default: { - SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", - pv4l2_control->id); - kfree(pv4l2_control); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - } - if (copy_to_user((void __user *)arg, pv4l2_control, - sizeof(struct v4l2_control))) { - kfree(pv4l2_control); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - kfree(pv4l2_control); - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_S_CTRL: { - struct v4l2_control v4l2_control; - - JOM(8, "VIDIOC_S_CTRL\n"); - - if (0 != copy_from_user(&v4l2_control, (void __user *)arg, - sizeof(struct v4l2_control))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - switch (v4l2_control.id) { - case V4L2_CID_BRIGHTNESS: { - JOM(8, "user requests brightness %i\n", v4l2_control.value); - if (0 != adjust_brightness(peasycap, v4l2_control.value)) - ; - break; - } - case V4L2_CID_CONTRAST: { - JOM(8, "user requests contrast %i\n", v4l2_control.value); - if (0 != adjust_contrast(peasycap, v4l2_control.value)) - ; - break; - } - case V4L2_CID_SATURATION: { - JOM(8, "user requests saturation %i\n", v4l2_control.value); - if (0 != adjust_saturation(peasycap, v4l2_control.value)) - ; - break; - } - case V4L2_CID_HUE: { - JOM(8, "user requests hue %i\n", v4l2_control.value); - if (0 != adjust_hue(peasycap, v4l2_control.value)) - ; - break; - } - case V4L2_CID_AUDIO_VOLUME: { - JOM(8, "user requests volume %i\n", v4l2_control.value); - if (0 != adjust_volume(peasycap, v4l2_control.value)) - ; - break; - } - case V4L2_CID_AUDIO_MUTE: { - int mute; - - JOM(8, "user requests mute %i\n", v4l2_control.value); - if (v4l2_control.value) - mute = 1; - else - mute = 0; - - if (0 != adjust_mute(peasycap, mute)) - SAM("WARNING: failed to adjust mute to %i\n", mute); - break; - } - default: { - SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", - v4l2_control.id); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_S_EXT_CTRLS: { - JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_ENUM_FMT: { - u32 index; - struct v4l2_fmtdesc v4l2_fmtdesc; - - JOM(8, "VIDIOC_ENUM_FMT\n"); - - if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg, - sizeof(struct v4l2_fmtdesc))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - index = v4l2_fmtdesc.index; - memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc)); - - v4l2_fmtdesc.index = index; - v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - switch (index) { - case 0: { - v4l2_fmtdesc.flags = 0; - strcpy(&v4l2_fmtdesc.description[0], "uyvy"); - v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_UYVY; - JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); - break; - } - case 1: { - v4l2_fmtdesc.flags = 0; - strcpy(&v4l2_fmtdesc.description[0], "yuy2"); - v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV; - JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); - break; - } - case 2: { - v4l2_fmtdesc.flags = 0; - strcpy(&v4l2_fmtdesc.description[0], "rgb24"); - v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB24; - JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); - break; - } - case 3: { - v4l2_fmtdesc.flags = 0; - strcpy(&v4l2_fmtdesc.description[0], "rgb32"); - v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB32; - JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); - break; - } - case 4: { - v4l2_fmtdesc.flags = 0; - strcpy(&v4l2_fmtdesc.description[0], "bgr24"); - v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR24; - JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); - break; - } - case 5: { - v4l2_fmtdesc.flags = 0; - strcpy(&v4l2_fmtdesc.description[0], "bgr32"); - v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR32; - JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); - break; - } - default: { - JOM(8, "%i=index: exhausts formats\n", index); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - } - if (copy_to_user((void __user *)arg, &v4l2_fmtdesc, - sizeof(struct v4l2_fmtdesc))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* - * THE RESPONSE TO VIDIOC_ENUM_FRAMESIZES MUST BE CONDITIONED ON THE - * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE. - */ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_ENUM_FRAMESIZES: { - u32 index; - struct v4l2_frmsizeenum v4l2_frmsizeenum; - - JOM(8, "VIDIOC_ENUM_FRAMESIZES\n"); - - if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg, - sizeof(struct v4l2_frmsizeenum))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - index = v4l2_frmsizeenum.index; - - v4l2_frmsizeenum.type = (u32) V4L2_FRMSIZE_TYPE_DISCRETE; - - if (peasycap->ntsc) { - switch (index) { - case 0: { - v4l2_frmsizeenum.discrete.width = 640; - v4l2_frmsizeenum.discrete.height = 480; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - case 1: { - v4l2_frmsizeenum.discrete.width = 320; - v4l2_frmsizeenum.discrete.height = 240; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - case 2: { - v4l2_frmsizeenum.discrete.width = 720; - v4l2_frmsizeenum.discrete.height = 480; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - case 3: { - v4l2_frmsizeenum.discrete.width = 360; - v4l2_frmsizeenum.discrete.height = 240; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - default: { - JOM(8, "%i=index: exhausts framesizes\n", index); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - } - } else { - switch (index) { - case 0: { - v4l2_frmsizeenum.discrete.width = 640; - v4l2_frmsizeenum.discrete.height = 480; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - case 1: { - v4l2_frmsizeenum.discrete.width = 320; - v4l2_frmsizeenum.discrete.height = 240; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - case 2: { - v4l2_frmsizeenum.discrete.width = 704; - v4l2_frmsizeenum.discrete.height = 576; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - case 3: { - v4l2_frmsizeenum.discrete.width = 720; - v4l2_frmsizeenum.discrete.height = 576; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - case 4: { - v4l2_frmsizeenum.discrete.width = 360; - v4l2_frmsizeenum.discrete.height = 288; - JOM(8, "%i=index: %ix%i\n", index, - (int)(v4l2_frmsizeenum. - discrete.width), - (int)(v4l2_frmsizeenum. - discrete.height)); - break; - } - default: { - JOM(8, "%i=index: exhausts framesizes\n", index); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - } - } - if (copy_to_user((void __user *)arg, &v4l2_frmsizeenum, - sizeof(struct v4l2_frmsizeenum))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* - * THE RESPONSE TO VIDIOC_ENUM_FRAMEINTERVALS MUST BE CONDITIONED ON THE - * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE. - */ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_ENUM_FRAMEINTERVALS: { - u32 index; - int denominator; - struct v4l2_frmivalenum v4l2_frmivalenum; - - JOM(8, "VIDIOC_ENUM_FRAMEINTERVALS\n"); - - if (peasycap->fps) - denominator = peasycap->fps; - else { - if (peasycap->ntsc) - denominator = 30; - else - denominator = 25; - } - - if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg, - sizeof(struct v4l2_frmivalenum))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - index = v4l2_frmivalenum.index; - - v4l2_frmivalenum.type = (u32) V4L2_FRMIVAL_TYPE_DISCRETE; - - switch (index) { - case 0: { - v4l2_frmivalenum.discrete.numerator = 1; - v4l2_frmivalenum.discrete.denominator = denominator; - JOM(8, "%i=index: %i/%i\n", index, - (int)(v4l2_frmivalenum.discrete.numerator), - (int)(v4l2_frmivalenum.discrete.denominator)); - break; - } - case 1: { - v4l2_frmivalenum.discrete.numerator = 1; - v4l2_frmivalenum.discrete.denominator = denominator/5; - JOM(8, "%i=index: %i/%i\n", index, - (int)(v4l2_frmivalenum.discrete.numerator), - (int)(v4l2_frmivalenum.discrete.denominator)); - break; - } - default: { - JOM(8, "%i=index: exhausts frameintervals\n", index); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - } - if (copy_to_user((void __user *)arg, &v4l2_frmivalenum, - sizeof(struct v4l2_frmivalenum))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_FMT: { - struct v4l2_format *pv4l2_format; - struct v4l2_pix_format *pv4l2_pix_format; - - JOM(8, "VIDIOC_G_FMT\n"); - pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL); - if (!pv4l2_format) { - SAM("ERROR: out of memory\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ENOMEM; - } - pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL); - if (!pv4l2_pix_format) { - SAM("ERROR: out of memory\n"); - kfree(pv4l2_format); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ENOMEM; - } - if (0 != copy_from_user(pv4l2_format, (void __user *)arg, - sizeof(struct v4l2_format))) { - kfree(pv4l2_format); - kfree(pv4l2_pix_format); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - kfree(pv4l2_format); - kfree(pv4l2_pix_format); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - - memset(pv4l2_pix_format, 0, sizeof(struct v4l2_pix_format)); - pv4l2_format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - memcpy(&pv4l2_format->fmt.pix, - &easycap_format[peasycap->format_offset] - .v4l2_format.fmt.pix, sizeof(struct v4l2_pix_format)); - JOM(8, "user is told: %s\n", - &easycap_format[peasycap->format_offset].name[0]); - - if (copy_to_user((void __user *)arg, pv4l2_format, - sizeof(struct v4l2_format))) { - kfree(pv4l2_format); - kfree(pv4l2_pix_format); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - kfree(pv4l2_format); - kfree(pv4l2_pix_format); - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_TRY_FMT: - case VIDIOC_S_FMT: { - struct v4l2_format v4l2_format; - struct v4l2_pix_format v4l2_pix_format; - bool try; - int best_format; - - if (VIDIOC_TRY_FMT == cmd) { - JOM(8, "VIDIOC_TRY_FMT\n"); - try = true; - } else { - JOM(8, "VIDIOC_S_FMT\n"); - try = false; - } - - if (0 != copy_from_user(&v4l2_format, (void __user *)arg, - sizeof(struct v4l2_format))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - best_format = adjust_format(peasycap, - v4l2_format.fmt.pix.width, - v4l2_format.fmt.pix.height, - v4l2_format.fmt.pix.pixelformat, - v4l2_format.fmt.pix.field, - try); - if (0 > best_format) { - if (-EBUSY == best_format) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EBUSY; - } - JOM(8, "WARNING: adjust_format() returned %i\n", best_format); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ENOENT; - } -/*...........................................................................*/ - memset(&v4l2_pix_format, 0, sizeof(struct v4l2_pix_format)); - v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - memcpy(&(v4l2_format.fmt.pix), - &(easycap_format[best_format].v4l2_format.fmt.pix), - sizeof(v4l2_pix_format)); - JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]); - - if (copy_to_user((void __user *)arg, &v4l2_format, - sizeof(struct v4l2_format))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_CROPCAP: { - struct v4l2_cropcap v4l2_cropcap; - - JOM(8, "VIDIOC_CROPCAP\n"); - - if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg, - sizeof(struct v4l2_cropcap))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); - - memset(&v4l2_cropcap, 0, sizeof(struct v4l2_cropcap)); - v4l2_cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_cropcap.bounds.left = 0; - v4l2_cropcap.bounds.top = 0; - v4l2_cropcap.bounds.width = peasycap->width; - v4l2_cropcap.bounds.height = peasycap->height; - v4l2_cropcap.defrect.left = 0; - v4l2_cropcap.defrect.top = 0; - v4l2_cropcap.defrect.width = peasycap->width; - v4l2_cropcap.defrect.height = peasycap->height; - v4l2_cropcap.pixelaspect.numerator = 1; - v4l2_cropcap.pixelaspect.denominator = 1; - - JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height); - - if (copy_to_user((void __user *)arg, &v4l2_cropcap, - sizeof(struct v4l2_cropcap))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_CROP: - case VIDIOC_S_CROP: { - JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_QUERYSTD: { - JOM(8, "VIDIOC_QUERYSTD: " - "EasyCAP is incapable of detecting standard\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - break; - } - /*-------------------------------------------------------------------*/ - /* - * THE MANIPULATIONS INVOLVING last0,last1,last2,last3 - * CONSTITUTE A WORKAROUND * FOR WHAT APPEARS TO BE - * A BUG IN 64-BIT mplayer. - * NOT NEEDED, BUT HOPEFULLY HARMLESS, FOR 32-BIT mplayer. - */ - /*------------------------------------------------------------------*/ - case VIDIOC_ENUMSTD: { - int last0 = -1, last1 = -1, last2 = -1, last3 = -1; - struct v4l2_standard v4l2_standard; - u32 index; - struct easycap_standard const *peasycap_standard; - - JOM(8, "VIDIOC_ENUMSTD\n"); - - if (0 != copy_from_user(&v4l2_standard, (void __user *)arg, - sizeof(struct v4l2_standard))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - index = v4l2_standard.index; - - last3 = last2; - last2 = last1; - last1 = last0; - last0 = index; - if ((index == last3) && (index == last2) && - (index == last1) && (index == last0)) { - index++; - last3 = last2; - last2 = last1; - last1 = last0; - last0 = index; - } - - memset(&v4l2_standard, 0, sizeof(struct v4l2_standard)); - - peasycap_standard = &easycap_standard[0]; - while (0xFFFF != peasycap_standard->mask) { - if ((int)(peasycap_standard - &easycap_standard[0]) == index) - break; - peasycap_standard++; - } - if (0xFFFF == peasycap_standard->mask) { - JOM(8, "%i=index: exhausts standards\n", index); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - JOM(8, "%i=index: %s\n", index, - &(peasycap_standard->v4l2_standard.name[0])); - memcpy(&v4l2_standard, &(peasycap_standard->v4l2_standard), - sizeof(struct v4l2_standard)); - - v4l2_standard.index = index; - - if (copy_to_user((void __user *)arg, &v4l2_standard, - sizeof(struct v4l2_standard))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_STD: { - v4l2_std_id std_id; - struct easycap_standard const *peasycap_standard; - - JOM(8, "VIDIOC_G_STD\n"); - - if (0 > peasycap->standard_offset) { - JOM(8, "%i=peasycap->standard_offset\n", - peasycap->standard_offset); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EBUSY; - } - - if (0 != copy_from_user(&std_id, (void __user *)arg, - sizeof(v4l2_std_id))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - peasycap_standard = &easycap_standard[peasycap->standard_offset]; - std_id = peasycap_standard->v4l2_standard.id; - - JOM(8, "user is told: %s\n", - &peasycap_standard->v4l2_standard.name[0]); - - if (copy_to_user((void __user *)arg, &std_id, - sizeof(v4l2_std_id))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_S_STD: { - v4l2_std_id std_id; - int rc; - - JOM(8, "VIDIOC_S_STD\n"); - - if (0 != copy_from_user(&std_id, (void __user *)arg, - sizeof(v4l2_std_id))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - JOM(8, "User requests standard: 0x%08X%08X\n", - (int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32), - (int)(std_id & ((v4l2_std_id)0xFFFFFFFF))); - - rc = adjust_standard(peasycap, std_id); - if (0 > rc) { - JOM(8, "WARNING: adjust_standard() returned %i\n", rc); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ENOENT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_REQBUFS: { - int nbuffers; - struct v4l2_requestbuffers v4l2_requestbuffers; - - JOM(8, "VIDIOC_REQBUFS\n"); - - if (0 != copy_from_user(&v4l2_requestbuffers, - (void __user *)arg, - sizeof(struct v4l2_requestbuffers))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - nbuffers = v4l2_requestbuffers.count; - JOM(8, " User requests %i buffers ...\n", nbuffers); - if (nbuffers < 2) - nbuffers = 2; - if (nbuffers > FRAME_BUFFER_MANY) - nbuffers = FRAME_BUFFER_MANY; - if (v4l2_requestbuffers.count == nbuffers) { - JOM(8, " ... agree to %i buffers\n", - nbuffers); - } else { - JOM(8, " ... insist on %i buffers\n", - nbuffers); - v4l2_requestbuffers.count = nbuffers; - } - peasycap->frame_buffer_many = nbuffers; - - if (copy_to_user((void __user *)arg, &v4l2_requestbuffers, - sizeof(struct v4l2_requestbuffers))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_QUERYBUF: { - u32 index; - struct v4l2_buffer v4l2_buffer; - - JOM(8, "VIDIOC_QUERYBUF\n"); - - if (peasycap->video_eof) { - JOM(8, "returning -EIO because %i=video_eof\n", - peasycap->video_eof); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EIO; - } - - if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, - sizeof(struct v4l2_buffer))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - index = v4l2_buffer.index; - if (index < 0 || index >= peasycap->frame_buffer_many) - return -EINVAL; - memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer)); - v4l2_buffer.index = index; - v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buffer.bytesused = peasycap->frame_buffer_used; - v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | - peasycap->done[index] | - peasycap->queued[index]; - v4l2_buffer.field = V4L2_FIELD_NONE; - v4l2_buffer.memory = V4L2_MEMORY_MMAP; - v4l2_buffer.m.offset = index * FRAME_BUFFER_SIZE; - v4l2_buffer.length = FRAME_BUFFER_SIZE; - - JOM(16, " %10i=index\n", v4l2_buffer.index); - JOM(16, " 0x%08X=type\n", v4l2_buffer.type); - JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused); - JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags); - JOM(16, " %10i=field\n", v4l2_buffer.field); - JOM(16, " %10li=timestamp.tv_usec\n", - (long)v4l2_buffer.timestamp.tv_usec); - JOM(16, " %10i=sequence\n", v4l2_buffer.sequence); - JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory); - JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset); - JOM(16, " %10i=length\n", v4l2_buffer.length); - - if (copy_to_user((void __user *)arg, &v4l2_buffer, - sizeof(struct v4l2_buffer))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_QBUF: { - struct v4l2_buffer v4l2_buffer; - - JOM(8, "VIDIOC_QBUF\n"); - - if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, - sizeof(struct v4l2_buffer))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - if (v4l2_buffer.index < 0 || - v4l2_buffer.index >= peasycap->frame_buffer_many) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED; - - peasycap->done[v4l2_buffer.index] = 0; - peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED; - - if (copy_to_user((void __user *)arg, &v4l2_buffer, - sizeof(struct v4l2_buffer))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - JOM(8, "..... user queueing frame buffer %i\n", - (int)v4l2_buffer.index); - - peasycap->frame_lock = 0; - - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_DQBUF: - { - struct timeval timeval, timeval2; - int i, j; - struct v4l2_buffer v4l2_buffer; - int rcdq; - u16 input; - - JOM(8, "VIDIOC_DQBUF\n"); - - if ((peasycap->video_idle) || (peasycap->video_eof)) { - JOM(8, "returning -EIO because " - "%i=video_idle %i=video_eof\n", - peasycap->video_idle, peasycap->video_eof); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EIO; - } - - if (copy_from_user(&v4l2_buffer, (void __user *)arg, - sizeof(struct v4l2_buffer))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - - if (peasycap->offerfields) { - /*---------------------------------------------------*/ - /* - * IN ITS 50 "fps" MODE tvtime SEEMS ALWAYS TO REQUEST - * V4L2_FIELD_BOTTOM - */ - /*---------------------------------------------------*/ - if (V4L2_FIELD_TOP == v4l2_buffer.field) - JOM(8, "user wants V4L2_FIELD_TOP\n"); - else if (V4L2_FIELD_BOTTOM == v4l2_buffer.field) - JOM(8, "user wants V4L2_FIELD_BOTTOM\n"); - else if (V4L2_FIELD_ANY == v4l2_buffer.field) - JOM(8, "user wants V4L2_FIELD_ANY\n"); - else - JOM(8, "user wants V4L2_FIELD_...UNKNOWN: %i\n", - v4l2_buffer.field); - } - - if (!peasycap->video_isoc_streaming) { - JOM(16, "returning -EIO because video urbs not streaming\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EIO; - } - /*-------------------------------------------------------------------*/ - /* - * IF THE USER HAS PREVIOUSLY CALLED easycap_poll(), - * AS DETERMINED BY FINDING - * THE FLAG peasycap->polled SET, THERE MUST BE - * NO FURTHER WAIT HERE. IN THIS - * CASE, JUST CHOOSE THE FRAME INDICATED BY peasycap->frame_read - */ - /*-------------------------------------------------------------------*/ - - if (!peasycap->polled) { - do { - rcdq = easycap_dqbuf(peasycap, 0); - if (-EIO == rcdq) { - JOM(8, "returning -EIO because " - "dqbuf() returned -EIO\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EIO; - } - } while (0 != rcdq); - } else { - if (peasycap->video_eof) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EIO; - } - } - if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) { - JOM(8, "V4L2_BUF_FLAG_DONE != 0x%08X\n", - peasycap->done[peasycap->frame_read]); - } - peasycap->polled = 0; - - if (!(peasycap->isequence % 10)) { - for (i = 0; i < 179; i++) - peasycap->merit[i] = peasycap->merit[i+1]; - peasycap->merit[179] = merit_saa(peasycap->pusb_device); - j = 0; - for (i = 0; i < 180; i++) - j += peasycap->merit[i]; - if (90 < j) { - SAM("easycap driver shutting down " - "on condition blue\n"); - peasycap->video_eof = 1; - peasycap->audio_eof = 1; - } - } - - v4l2_buffer.index = peasycap->frame_read; - v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - v4l2_buffer.bytesused = peasycap->frame_buffer_used; - v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; - if (peasycap->offerfields) - v4l2_buffer.field = V4L2_FIELD_BOTTOM; - else - v4l2_buffer.field = V4L2_FIELD_NONE; - do_gettimeofday(&timeval); - timeval2 = timeval; - - v4l2_buffer.timestamp = timeval2; - v4l2_buffer.sequence = peasycap->isequence++; - v4l2_buffer.memory = V4L2_MEMORY_MMAP; - v4l2_buffer.m.offset = v4l2_buffer.index * FRAME_BUFFER_SIZE; - v4l2_buffer.length = FRAME_BUFFER_SIZE; - - JOM(16, " %10i=index\n", v4l2_buffer.index); - JOM(16, " 0x%08X=type\n", v4l2_buffer.type); - JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused); - JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags); - JOM(16, " %10i=field\n", v4l2_buffer.field); - JOM(16, " %10li=timestamp.tv_sec\n", - (long)v4l2_buffer.timestamp.tv_sec); - JOM(16, " %10li=timestamp.tv_usec\n", - (long)v4l2_buffer.timestamp.tv_usec); - JOM(16, " %10i=sequence\n", v4l2_buffer.sequence); - JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory); - JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset); - JOM(16, " %10i=length\n", v4l2_buffer.length); - - if (copy_to_user((void __user *)arg, &v4l2_buffer, - sizeof(struct v4l2_buffer))) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - input = peasycap->frame_buffer[peasycap->frame_read][0].input; - if (0x08 & input) { - JOM(8, "user is offered frame buffer %i, input %i\n", - peasycap->frame_read, (0x07 & input)); - } else { - JOM(8, "user is offered frame buffer %i\n", - peasycap->frame_read); - } - peasycap->frame_lock = 1; - JOM(8, "%i=peasycap->frame_fill\n", peasycap->frame_fill); - if (peasycap->frame_read == peasycap->frame_fill) { - if (peasycap->frame_lock) { - JOM(8, "WORRY: filling frame buffer " - "while offered to user\n"); - } - } - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_STREAMON: { - int i; - - JOM(8, "VIDIOC_STREAMON\n"); - - peasycap->isequence = 0; - for (i = 0; i < 180; i++) - peasycap->merit[i] = 0; - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - submit_video_urbs(peasycap); - peasycap->video_idle = 0; - peasycap->audio_idle = 0; - peasycap->video_eof = 0; - peasycap->audio_eof = 0; - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_STREAMOFF: { - JOM(8, "VIDIOC_STREAMOFF\n"); - - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - - peasycap->video_idle = 1; - peasycap->audio_idle = 1; -/*---------------------------------------------------------------------------*/ -/* - * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO THE STREAMOFF COMMAND - * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE. - */ -/*---------------------------------------------------------------------------*/ - JOM(8, "calling wake_up on wq_video and wq_audio\n"); - wake_up_interruptible(&(peasycap->wq_video)); - if (peasycap->psubstream) - snd_pcm_period_elapsed(peasycap->psubstream); - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_PARM: { - struct v4l2_streamparm *pv4l2_streamparm; - - JOM(8, "VIDIOC_G_PARM\n"); - pv4l2_streamparm = memdup_user((void __user *)arg, - sizeof(struct v4l2_streamparm)); - if (IS_ERR(pv4l2_streamparm)) { - SAM("ERROR: copy from user failed\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return PTR_ERR(pv4l2_streamparm); - } - - if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - kfree(pv4l2_streamparm); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - pv4l2_streamparm->parm.capture.capability = 0; - pv4l2_streamparm->parm.capture.capturemode = 0; - pv4l2_streamparm->parm.capture.timeperframe.numerator = 1; - - if (peasycap->fps) { - pv4l2_streamparm->parm.capture.timeperframe. - denominator = peasycap->fps; - } else { - if (peasycap->ntsc) { - pv4l2_streamparm->parm.capture.timeperframe. - denominator = 30; - } else { - pv4l2_streamparm->parm.capture.timeperframe. - denominator = 25; - } - } - - pv4l2_streamparm->parm.capture.readbuffers = - peasycap->frame_buffer_many; - pv4l2_streamparm->parm.capture.extendedmode = 0; - if (copy_to_user((void __user *)arg, - pv4l2_streamparm, - sizeof(struct v4l2_streamparm))) { - kfree(pv4l2_streamparm); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EFAULT; - } - kfree(pv4l2_streamparm); - break; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_S_PARM: { - JOM(8, "VIDIOC_S_PARM unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_AUDIO: { - JOM(8, "VIDIOC_G_AUDIO unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_S_AUDIO: { - JOM(8, "VIDIOC_S_AUDIO unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_S_TUNER: { - JOM(8, "VIDIOC_S_TUNER unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_FBUF: - case VIDIOC_S_FBUF: - case VIDIOC_OVERLAY: { - JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - case VIDIOC_G_TUNER: { - JOM(8, "VIDIOC_G_TUNER unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } - case VIDIOC_G_FREQUENCY: - case VIDIOC_S_FREQUENCY: { - JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -EINVAL; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - default: { - JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ENOIOCTLCMD; - } - } - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - JOM(4, "unlocked easycapdc60_dongle[%i].mutex_video\n", kd); - return 0; -} -/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_low.c b/drivers/staging/easycap/easycap_low.c deleted file mode 100644 index 0385735ac6df..000000000000 --- a/drivers/staging/easycap/easycap_low.c +++ /dev/null @@ -1,1129 +0,0 @@ -/***************************************************************************** -* * -* * -* easycap_low.c * -* * -* * -*****************************************************************************/ -/* - * - * Copyright (C) 2010 R.M. Thomas - * - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ -/*****************************************************************************/ -/* - * ACKNOWLEGEMENTS AND REFERENCES - * ------------------------------ - * This driver makes use of register information contained in the Syntek - * Semicon DC-1125 driver hosted at - * http://sourceforge.net/projects/syntekdriver/. - * Particularly useful has been a patch to the latter driver provided by - * Ivor Hewitt in January 2009. The NTSC implementation is taken from the - * work of Ben Trask. -*/ -/****************************************************************************/ - -#include "easycap.h" - -#define GET(X, Y, Z) do { \ - int __rc; \ - *(Z) = (u16)0; \ - __rc = regget(X, Y, Z, sizeof(u8)); \ - if (0 > __rc) { \ - JOT(8, ":-(%i\n", __LINE__); return __rc; \ - } \ -} while (0) - -#define SET(X, Y, Z) do { \ - int __rc; \ - __rc = regset(X, Y, Z); \ - if (0 > __rc) { \ - JOT(8, ":-(%i\n", __LINE__); return __rc; \ - } \ -} while (0) - -/*--------------------------------------------------------------------------*/ -static const struct stk1160config { - int reg; - int set; -} stk1160configPAL[256] = { - {0x000, 0x0098}, - {0x002, 0x0093}, - - {0x001, 0x0003}, - {0x003, 0x0080}, - {0x00D, 0x0000}, - {0x00F, 0x0002}, - {0x018, 0x0010}, - {0x019, 0x0000}, - {0x01A, 0x0014}, - {0x01B, 0x000E}, - {0x01C, 0x0046}, - - {0x100, 0x0033}, - {0x103, 0x0000}, - {0x104, 0x0000}, - {0x105, 0x0000}, - {0x106, 0x0000}, - -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* - * RESOLUTION 640x480 -*/ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - {0x110, 0x0008}, - {0x111, 0x0000}, - {0x112, 0x0020}, - {0x113, 0x0000}, - {0x114, 0x0508}, - {0x115, 0x0005}, - {0x116, 0x0110}, - {0x117, 0x0001}, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - {0x202, 0x000F}, - {0x203, 0x004A}, - {0x2FF, 0x0000}, - - {0xFFF, 0xFFFF} -}; -/*--------------------------------------------------------------------------*/ -static const struct stk1160config stk1160configNTSC[256] = { - {0x000, 0x0098}, - {0x002, 0x0093}, - - {0x001, 0x0003}, - {0x003, 0x0080}, - {0x00D, 0x0000}, - {0x00F, 0x0002}, - {0x018, 0x0010}, - {0x019, 0x0000}, - {0x01A, 0x0014}, - {0x01B, 0x000E}, - {0x01C, 0x0046}, - - {0x100, 0x0033}, - {0x103, 0x0000}, - {0x104, 0x0000}, - {0x105, 0x0000}, - {0x106, 0x0000}, - -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ -/* - * RESOLUTION 640x480 -*/ -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - {0x110, 0x0008}, - {0x111, 0x0000}, - {0x112, 0x0003}, - {0x113, 0x0000}, - {0x114, 0x0508}, - {0x115, 0x0005}, - {0x116, 0x00F3}, - {0x117, 0x0000}, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - - {0x202, 0x000F}, - {0x203, 0x004A}, - {0x2FF, 0x0000}, - - {0xFFF, 0xFFFF} -}; -/*--------------------------------------------------------------------------*/ -static const struct saa7113config { - int reg; - int set; -} saa7113configPAL[256] = { - {0x01, 0x08}, - {0x02, 0x80}, - {0x03, 0x33}, - {0x04, 0x00}, - {0x05, 0x00}, - {0x06, 0xE9}, - {0x07, 0x0D}, - {0x08, 0x38}, - {0x09, 0x00}, - {0x0A, SAA_0A_DEFAULT}, - {0x0B, SAA_0B_DEFAULT}, - {0x0C, SAA_0C_DEFAULT}, - {0x0D, SAA_0D_DEFAULT}, - {0x0E, 0x01}, - {0x0F, 0x36}, - {0x10, 0x00}, - {0x11, 0x0C}, - {0x12, 0xE7}, - {0x13, 0x00}, - {0x15, 0x00}, - {0x16, 0x00}, - {0x40, 0x02}, - {0x41, 0xFF}, - {0x42, 0xFF}, - {0x43, 0xFF}, - {0x44, 0xFF}, - {0x45, 0xFF}, - {0x46, 0xFF}, - {0x47, 0xFF}, - {0x48, 0xFF}, - {0x49, 0xFF}, - {0x4A, 0xFF}, - {0x4B, 0xFF}, - {0x4C, 0xFF}, - {0x4D, 0xFF}, - {0x4E, 0xFF}, - {0x4F, 0xFF}, - {0x50, 0xFF}, - {0x51, 0xFF}, - {0x52, 0xFF}, - {0x53, 0xFF}, - {0x54, 0xFF}, - {0x55, 0xFF}, - {0x56, 0xFF}, - {0x57, 0xFF}, - {0x58, 0x40}, - {0x59, 0x54}, - {0x5A, 0x07}, - {0x5B, 0x83}, - - {0xFF, 0xFF} -}; -/*--------------------------------------------------------------------------*/ -static const struct saa7113config saa7113configNTSC[256] = { - {0x01, 0x08}, - {0x02, 0x80}, - {0x03, 0x33}, - {0x04, 0x00}, - {0x05, 0x00}, - {0x06, 0xE9}, - {0x07, 0x0D}, - {0x08, 0x78}, - {0x09, 0x00}, - {0x0A, SAA_0A_DEFAULT}, - {0x0B, SAA_0B_DEFAULT}, - {0x0C, SAA_0C_DEFAULT}, - {0x0D, SAA_0D_DEFAULT}, - {0x0E, 0x01}, - {0x0F, 0x36}, - {0x10, 0x00}, - {0x11, 0x0C}, - {0x12, 0xE7}, - {0x13, 0x00}, - {0x15, 0x00}, - {0x16, 0x00}, - {0x40, 0x82}, - {0x41, 0xFF}, - {0x42, 0xFF}, - {0x43, 0xFF}, - {0x44, 0xFF}, - {0x45, 0xFF}, - {0x46, 0xFF}, - {0x47, 0xFF}, - {0x48, 0xFF}, - {0x49, 0xFF}, - {0x4A, 0xFF}, - {0x4B, 0xFF}, - {0x4C, 0xFF}, - {0x4D, 0xFF}, - {0x4E, 0xFF}, - {0x4F, 0xFF}, - {0x50, 0xFF}, - {0x51, 0xFF}, - {0x52, 0xFF}, - {0x53, 0xFF}, - {0x54, 0xFF}, - {0x55, 0xFF}, - {0x56, 0xFF}, - {0x57, 0xFF}, - {0x58, 0x40}, - {0x59, 0x54}, - {0x5A, 0x0A}, - {0x5B, 0x83}, - - {0xFF, 0xFF} -}; - -static int regget(struct usb_device *pusb_device, - u16 index, void *reg, int reg_size) -{ - int rc; - - if (!pusb_device) - return -ENODEV; - - rc = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0), - 0x00, - (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE), - 0x00, - index, reg, reg_size, 50000); - - return rc; -} - -static int regset(struct usb_device *pusb_device, u16 index, u16 value) -{ - int rc; - - if (!pusb_device) - return -ENODEV; - - rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), - 0x01, - (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE), - value, index, NULL, 0, 500); - - if (rc < 0) - return rc; - - if (easycap_readback) { - u16 igot = 0; - rc = regget(pusb_device, index, &igot, sizeof(igot)); - igot = 0xFF & igot; - switch (index) { - case 0x000: - case 0x500: - case 0x502: - case 0x503: - case 0x504: - case 0x506: - case 0x507: - break; - - case 0x204: - case 0x205: - case 0x350: - case 0x351: - if (igot) - JOT(8, "unexpected 0x%02X " - "for STK register 0x%03X\n", - igot, index); - break; - - default: - if ((0xFF & value) != igot) - JOT(8, "unexpected 0x%02X != 0x%02X " - "for STK register 0x%03X\n", - igot, value, index); - break; - } - } - - return rc; -} -/*--------------------------------------------------------------------------*/ -/* - * FUNCTION wait_i2c() RETURNS 0 ON SUCCESS -*/ -/*--------------------------------------------------------------------------*/ -static int wait_i2c(struct usb_device *p) -{ - u16 get0; - u8 igot; - const int max = 2; - int k; - - if (!p) - return -ENODEV; - - for (k = 0; k < max; k++) { - GET(p, 0x0201, &igot); get0 = igot; - switch (get0) { - case 0x04: - case 0x01: - return 0; - case 0x00: - msleep(20); - continue; - default: - return get0 - 1; - } - } - return -1; -} - -/****************************************************************************/ -int confirm_resolution(struct usb_device *p) -{ - u8 get0, get1, get2, get3, get4, get5, get6, get7; - - if (!p) - return -ENODEV; - GET(p, 0x0110, &get0); - GET(p, 0x0111, &get1); - GET(p, 0x0112, &get2); - GET(p, 0x0113, &get3); - GET(p, 0x0114, &get4); - GET(p, 0x0115, &get5); - GET(p, 0x0116, &get6); - GET(p, 0x0117, &get7); - JOT(8, "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X\n", - get0, get1, get2, get3, get4, get5, get6, get7); - JOT(8, "....cf PAL_720x526: " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X\n", - 0x000, 0x000, 0x001, 0x000, 0x5A0, 0x005, 0x121, 0x001); - JOT(8, "....cf PAL_704x526: " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X\n", - 0x004, 0x000, 0x001, 0x000, 0x584, 0x005, 0x121, 0x001); - JOT(8, "....cf VGA_640x480: " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X, " - "0x%03X, 0x%03X\n", - 0x008, 0x000, 0x020, 0x000, 0x508, 0x005, 0x110, 0x001); - return 0; -} -/****************************************************************************/ -int confirm_stream(struct usb_device *p) -{ - u16 get2; - u8 igot; - - if (!p) - return -ENODEV; - GET(p, 0x0100, &igot); get2 = 0x80 & igot; - if (0x80 == get2) - JOT(8, "confirm_stream: OK\n"); - else - JOT(8, "confirm_stream: STUCK\n"); - return 0; -} -/****************************************************************************/ -int setup_stk(struct usb_device *p, bool ntsc) -{ - int i; - const struct stk1160config *cfg; - if (!p) - return -ENODEV; - cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL; - for (i = 0; cfg[i].reg != 0xFFF; i++) - SET(p, cfg[i].reg, cfg[i].set); - - write_300(p); - - return 0; -} -/****************************************************************************/ -int setup_saa(struct usb_device *p, bool ntsc) -{ - int i, ir; - const struct saa7113config *cfg; - if (!p) - return -ENODEV; - cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL; - for (i = 0; cfg[i].reg != 0xFF; i++) - ir = write_saa(p, cfg[i].reg, cfg[i].set); - return 0; -} -/****************************************************************************/ -int write_000(struct usb_device *p, u16 set2, u16 set0) -{ - u8 igot0, igot2; - - if (!p) - return -ENODEV; - GET(p, 0x0002, &igot2); - GET(p, 0x0000, &igot0); - SET(p, 0x0002, set2); - SET(p, 0x0000, set0); - return 0; -} -/****************************************************************************/ -int write_saa(struct usb_device *p, u16 reg0, u16 set0) -{ - if (!p) - return -ENODEV; - SET(p, 0x200, 0x00); - SET(p, 0x204, reg0); - SET(p, 0x205, set0); - SET(p, 0x200, 0x01); - return wait_i2c(p); -} -/****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?) - * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A - * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO SET - * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO SET - * REGISTER 504: TARGET ADDRESS ON VT1612A - */ -/*--------------------------------------------------------------------------*/ -int -write_vt(struct usb_device *p, u16 reg0, u16 set0) -{ - u8 igot; - u16 got502, got503; - u16 set502, set503; - - if (!p) - return -ENODEV; - SET(p, 0x0504, reg0); - SET(p, 0x0500, 0x008B); - - GET(p, 0x0502, &igot); got502 = (0xFF & igot); - GET(p, 0x0503, &igot); got503 = (0xFF & igot); - - JOT(16, "write_vt(., 0x%04X, 0x%04X): was 0x%04X\n", - reg0, set0, ((got503 << 8) | got502)); - - set502 = (0x00FF & set0); - set503 = ((0xFF00 & set0) >> 8); - - SET(p, 0x0504, reg0); - SET(p, 0x0502, set502); - SET(p, 0x0503, set503); - SET(p, 0x0500, 0x008C); - - return 0; -} -/****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?) - * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A - * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO GET - * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO GET - * REGISTER 504: TARGET ADDRESS ON VT1612A - */ -/*--------------------------------------------------------------------------*/ -int read_vt(struct usb_device *p, u16 reg0) -{ - u8 igot; - u16 got502, got503; - - if (!p) - return -ENODEV; - SET(p, 0x0504, reg0); - SET(p, 0x0500, 0x008B); - - GET(p, 0x0502, &igot); got502 = (0xFF & igot); - GET(p, 0x0503, &igot); got503 = (0xFF & igot); - - JOT(16, "read_vt(., 0x%04X): has 0x%04X\n", - reg0, ((got503 << 8) | got502)); - - return (got503 << 8) | got502; -} -/****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * THESE APPEAR TO HAVE NO EFFECT ON EITHER VIDEO OR AUDIO. - */ -/*--------------------------------------------------------------------------*/ -int write_300(struct usb_device *p) -{ - if (!p) - return -ENODEV; - SET(p, 0x300, 0x0012); - SET(p, 0x350, 0x002D); - SET(p, 0x351, 0x0001); - SET(p, 0x352, 0x0000); - SET(p, 0x353, 0x0000); - SET(p, 0x300, 0x0080); - return 0; -} -/****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * NOTE: THE FOLLOWING IS NOT CHECKED: - * REGISTER 0x0F, WHICH IS INVOLVED IN CHROMINANCE AUTOMATIC GAIN CONTROL. - */ -/*--------------------------------------------------------------------------*/ -int check_saa(struct usb_device *p, bool ntsc) -{ - int i, ir, rc = 0; - struct saa7113config const *cfg; - if (!p) - return -ENODEV; - - cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL; - for (i = 0; cfg[i].reg != 0xFF; i++) { - if (0x0F == cfg[i].reg) - continue; - ir = read_saa(p, cfg[i].reg); - if (ir != cfg[i].set) { - SAY("SAA register 0x%02X has 0x%02X, expected 0x%02X\n", - cfg[i].reg, ir, cfg[i].set); - rc--; - } - } - - return (rc < -8) ? rc : 0; -} -/****************************************************************************/ -int merit_saa(struct usb_device *p) -{ - int rc; - - if (!p) - return -ENODEV; - rc = read_saa(p, 0x1F); - return ((0 > rc) || (0x02 & rc)) ? 1 : 0; -} -/****************************************************************************/ -int ready_saa(struct usb_device *p) -{ - int j, rc, rate; - const int max = 5, marktime = PATIENCE/5; -/*--------------------------------------------------------------------------*/ -/* - * RETURNS 0 FOR INTERLACED 50 Hz - * 1 FOR NON-INTERLACED 50 Hz - * 2 FOR INTERLACED 60 Hz - * 3 FOR NON-INTERLACED 60 Hz -*/ -/*--------------------------------------------------------------------------*/ - if (!p) - return -ENODEV; - j = 0; - while (max > j) { - rc = read_saa(p, 0x1F); - if (0 <= rc) { - if (0 == (0x40 & rc)) - break; - if (1 == (0x01 & rc)) - break; - } - msleep(marktime); - j++; - } - if (max == j) - return -1; - else { - if (0x20 & rc) { - rate = 2; - JOT(8, "hardware detects 60 Hz\n"); - } else { - rate = 0; - JOT(8, "hardware detects 50 Hz\n"); - } - if (0x80 & rc) - JOT(8, "hardware detects interlacing\n"); - else { - rate++; - JOT(8, "hardware detects no interlacing\n"); - } - } - return 0; -} -/****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * NOTE: THE FOLLOWING ARE NOT CHECKED: - * REGISTERS 0x000, 0x002: FUNCTIONALITY IS NOT KNOWN - * REGISTER 0x100: ACCEPT ALSO (0x80 | stk1160config....[.].set) - */ -/*--------------------------------------------------------------------------*/ -int check_stk(struct usb_device *p, bool ntsc) -{ - int i, ir; - const struct stk1160config *cfg; - - if (!p) - return -ENODEV; - cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL; - - for (i = 0; 0xFFF != cfg[i].reg; i++) { - if (0x000 == cfg[i].reg || 0x002 == cfg[i].reg) - continue; - - - ir = read_stk(p, cfg[i].reg); - if (0x100 == cfg[i].reg) { - if ((ir != (0xFF & cfg[i].set)) && - (ir != (0x80 | (0xFF & cfg[i].set))) && - (0xFFFF != cfg[i].set)) { - SAY("STK reg[0x%03X]=0x%02X expected 0x%02X\n", - cfg[i].reg, ir, cfg[i].set); - } - continue; - } - if ((ir != (0xFF & cfg[i].set)) && (0xFFFF != cfg[i].set)) - SAY("STK register 0x%03X has 0x%02X,expected 0x%02X\n", - cfg[i].reg, ir, cfg[i].set); - } - return 0; -} -/****************************************************************************/ -int read_saa(struct usb_device *p, u16 reg0) -{ - u8 igot; - - if (!p) - return -ENODEV; - SET(p, 0x208, reg0); - SET(p, 0x200, 0x20); - if (0 != wait_i2c(p)) - return -1; - igot = 0; - GET(p, 0x0209, &igot); - return igot; -} -/****************************************************************************/ -int read_stk(struct usb_device *p, u32 reg0) -{ - u8 igot; - - if (!p) - return -ENODEV; - igot = 0; - GET(p, reg0, &igot); - return igot; -} -/****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * HARDWARE USERSPACE INPUT NUMBER PHYSICAL INPUT DRIVER input VALUE - * - * CVBS+S-VIDEO 0 or 1 CVBS 1 - * FOUR-CVBS 0 or 1 CVBS1 1 - * FOUR-CVBS 2 CVBS2 2 - * FOUR-CVBS 3 CVBS3 3 - * FOUR-CVBS 4 CVBS4 4 - * CVBS+S-VIDEO 5 S-VIDEO 5 - * - * WHEN 5==input THE ARGUMENT mode MUST ALSO BE SUPPLIED: - * - * mode 7 => GAIN TO BE SET EXPLICITLY USING REGISTER 0x05 (UNTESTED) - * mode 9 => USE AUTOMATIC GAIN CONTROL (DEFAULT) - * -*/ -/*---------------------------------------------------------------------------*/ -int -select_input(struct usb_device *p, int input, int mode) -{ - int ir; - - if (!p) - return -ENODEV; - stop_100(p); - switch (input) { - case 0: - case 1: { - if (0 != write_saa(p, 0x02, 0x80)) - SAY("ERROR: failed to set SAA register 0x02 " - "for input %i\n", input); - - SET(p, 0x0000, 0x0098); - SET(p, 0x0002, 0x0078); - break; - } - case 2: { - if (0 != write_saa(p, 0x02, 0x80)) - SAY("ERROR: failed to set SAA register 0x02 " - "for input %i\n", input); - - SET(p, 0x0000, 0x0090); - SET(p, 0x0002, 0x0078); - break; - } - case 3: { - if (0 != write_saa(p, 0x02, 0x80)) - SAY("ERROR: failed to set SAA register 0x02 " - " for input %i\n", input); - - SET(p, 0x0000, 0x0088); - SET(p, 0x0002, 0x0078); - break; - } - case 4: { - if (0 != write_saa(p, 0x02, 0x80)) { - SAY("ERROR: failed to set SAA register 0x02 " - "for input %i\n", input); - } - SET(p, 0x0000, 0x0080); - SET(p, 0x0002, 0x0078); - break; - } - case 5: { - if (9 != mode) - mode = 7; - switch (mode) { - case 7: { - if (0 != write_saa(p, 0x02, 0x87)) - SAY("ERROR: failed to set SAA register 0x02 " - "for input %i\n", input); - - if (0 != write_saa(p, 0x05, 0xFF)) - SAY("ERROR: failed to set SAA register 0x05 " - "for input %i\n", input); - - break; - } - case 9: { - if (0 != write_saa(p, 0x02, 0x89)) - SAY("ERROR: failed to set SAA register 0x02 " - "for input %i\n", input); - - if (0 != write_saa(p, 0x05, 0x00)) - SAY("ERROR: failed to set SAA register 0x05 " - "for input %i\n", input); - - break; - } - default: - SAY("MISTAKE: bad mode: %i\n", mode); - return -1; - } - - if (0 != write_saa(p, 0x04, 0x00)) - SAY("ERROR: failed to set SAA register 0x04 " - "for input %i\n", input); - - if (0 != write_saa(p, 0x09, 0x80)) - SAY("ERROR: failed to set SAA register 0x09 " - "for input %i\n", input); - - SET(p, 0x0002, 0x0093); - break; - } - default: - SAY("ERROR: bad input: %i\n", input); - return -1; - } - - ir = read_stk(p, 0x00); - JOT(8, "STK register 0x00 has 0x%02X\n", ir); - ir = read_saa(p, 0x02); - JOT(8, "SAA register 0x02 has 0x%02X\n", ir); - - start_100(p); - - return 0; -} -/****************************************************************************/ -int set_resolution(struct usb_device *p, - u16 set0, u16 set1, u16 set2, u16 set3) -{ - u16 u0x0111, u0x0113, u0x0115, u0x0117; - - if (!p) - return -ENODEV; - u0x0111 = ((0xFF00 & set0) >> 8); - u0x0113 = ((0xFF00 & set1) >> 8); - u0x0115 = ((0xFF00 & set2) >> 8); - u0x0117 = ((0xFF00 & set3) >> 8); - - SET(p, 0x0110, (0x00FF & set0)); - SET(p, 0x0111, u0x0111); - SET(p, 0x0112, (0x00FF & set1)); - SET(p, 0x0113, u0x0113); - SET(p, 0x0114, (0x00FF & set2)); - SET(p, 0x0115, u0x0115); - SET(p, 0x0116, (0x00FF & set3)); - SET(p, 0x0117, u0x0117); - - return 0; -} -/****************************************************************************/ -int start_100(struct usb_device *p) -{ - u16 get116, get117, get0; - u8 igot116, igot117, igot; - - if (!p) - return -ENODEV; - GET(p, 0x0116, &igot116); - get116 = igot116; - GET(p, 0x0117, &igot117); - get117 = igot117; - SET(p, 0x0116, 0x0000); - SET(p, 0x0117, 0x0000); - - GET(p, 0x0100, &igot); - get0 = igot; - SET(p, 0x0100, (0x80 | get0)); - - SET(p, 0x0116, get116); - SET(p, 0x0117, get117); - - return 0; -} -/****************************************************************************/ -int stop_100(struct usb_device *p) -{ - u16 get0; - u8 igot; - - if (!p) - return -ENODEV; - GET(p, 0x0100, &igot); - get0 = igot; - SET(p, 0x0100, (0x7F & get0)); - return 0; -} -/****************************************************************************/ -/****************************************************************************/ -/*****************************************************************************/ -int wakeup_device(struct usb_device *pusb_device) -{ - if (!pusb_device) - return -ENODEV; - return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), - USB_REQ_SET_FEATURE, - USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, - USB_DEVICE_REMOTE_WAKEUP, - 0, NULL, 0, 50000); -} -/*****************************************************************************/ -int -audio_setup(struct easycap *peasycap) -{ - struct usb_device *pusb_device; - u8 buffer[1]; - int rc, id1, id2; -/*---------------------------------------------------------------------------*/ -/* - * IMPORTANT: - * THE MESSAGE OF TYPE (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) - * CAUSES MUTING IF THE VALUE 0x0100 IS SENT. - * TO ENABLE AUDIO THE VALUE 0x0200 MUST BE SENT. - */ -/*---------------------------------------------------------------------------*/ - const u8 request = 0x01; - const u8 requesttype = USB_DIR_OUT | - USB_TYPE_CLASS | - USB_RECIP_INTERFACE; - const u16 value_unmute = 0x0200; - const u16 index = 0x0301; - const u16 length = 1; - - if (!peasycap) - return -EFAULT; - - pusb_device = peasycap->pusb_device; - if (!pusb_device) - return -ENODEV; - - JOM(8, "%02X %02X %02X %02X %02X %02X %02X %02X\n", - requesttype, request, - (0x00FF & value_unmute), - (0xFF00 & value_unmute) >> 8, - (0x00FF & index), - (0xFF00 & index) >> 8, - (0x00FF & length), - (0xFF00 & length) >> 8); - - buffer[0] = 0x01; - - rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), - request, requesttype, value_unmute, - index, &buffer[0], length, 50000); - - JOT(8, "0x%02X=buffer\n", buffer[0]); - if (rc != (int)length) { - switch (rc) { - case -EPIPE: - SAY("usb_control_msg returned -EPIPE\n"); - break; - default: - SAY("ERROR: usb_control_msg returned %i\n", rc); - break; - } - } -/*--------------------------------------------------------------------------*/ -/* - * REGISTER 500: SETTING VALUE TO 0x0094 RESETS AUDIO CONFIGURATION ??? - * REGISTER 506: ANALOGUE AUDIO ATTENTUATOR ??? - * FOR THE CVBS+S-VIDEO HARDWARE: - * SETTING VALUE TO 0x0000 GIVES QUIET SOUND. - * THE UPPER BYTE SEEMS TO HAVE NO EFFECT. - * FOR THE FOUR-CVBS HARDWARE: - * SETTING VALUE TO 0x0000 SEEMS TO HAVE NO EFFECT. - * REGISTER 507: ANALOGUE AUDIO PREAMPLIFIER ON/OFF ??? - * FOR THE CVBS-S-VIDEO HARDWARE: - * SETTING VALUE TO 0x0001 GIVES VERY LOUD, DISTORTED SOUND. - * THE UPPER BYTE SEEMS TO HAVE NO EFFECT. - */ -/*--------------------------------------------------------------------------*/ - SET(pusb_device, 0x0500, 0x0094); - SET(pusb_device, 0x0500, 0x008C); - SET(pusb_device, 0x0506, 0x0001); - SET(pusb_device, 0x0507, 0x0000); - id1 = read_vt(pusb_device, 0x007C); - id2 = read_vt(pusb_device, 0x007E); - SAM("0x%04X:0x%04X is audio vendor id\n", id1, id2); -/*---------------------------------------------------------------------------*/ -/* - * SELECT AUDIO SOURCE "LINE IN" AND SET THE AUDIO GAIN. -*/ -/*---------------------------------------------------------------------------*/ - if (0 != audio_gainset(pusb_device, peasycap->gain)) - SAY("ERROR: audio_gainset() failed\n"); - check_vt(pusb_device); - return 0; -} -/*****************************************************************************/ -int check_vt(struct usb_device *pusb_device) -{ - int igot; - - if (!pusb_device) - return -ENODEV; - igot = read_vt(pusb_device, 0x0002); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x02\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x02); - - igot = read_vt(pusb_device, 0x000E); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x0E\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x0E); - - igot = read_vt(pusb_device, 0x0010); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x10\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x10); - - igot = read_vt(pusb_device, 0x0012); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x12\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x12); - - igot = read_vt(pusb_device, 0x0014); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x14\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x14); - - igot = read_vt(pusb_device, 0x0016); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x16\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x16); - - igot = read_vt(pusb_device, 0x0018); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x18\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x18); - - igot = read_vt(pusb_device, 0x001C); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x1C\n"); - if (0x8000 & igot) - SAY("register 0x%02X muted\n", 0x1C); - - return 0; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* NOTE: THIS DOES INCREASE THE VOLUME DRAMATICALLY: - * audio_gainset(pusb_device, 0x000F); - * - * loud dB register 0x10 dB register 0x1C dB total - * 0 -34.5 0 -34.5 - * .. .... . .... - * 15 10.5 0 10.5 - * 16 12.0 0 12.0 - * 17 12.0 1.5 13.5 - * .. .... .... .... - * 31 12.0 22.5 34.5 -*/ -/*---------------------------------------------------------------------------*/ -int audio_gainset(struct usb_device *pusb_device, s8 loud) -{ - int igot; - u8 tmp; - u16 mute; - - if (!pusb_device) - return -ENODEV; - if (0 > loud) - loud = 0; - if (31 < loud) - loud = 31; - - write_vt(pusb_device, 0x0002, 0x8000); -/*---------------------------------------------------------------------------*/ - igot = read_vt(pusb_device, 0x000E); - if (0 > igot) { - SAY("ERROR: failed to read VT1612A register 0x0E\n"); - mute = 0x0000; - } else - mute = 0x8000 & ((unsigned int)igot); - mute = 0; - - if (16 > loud) - tmp = 0x01 | (0x001F & (((u8)(15 - loud)) << 1)); - else - tmp = 0; - - JOT(8, "0x%04X=(mute|tmp) for VT1612A register 0x0E\n", mute | tmp); - write_vt(pusb_device, 0x000E, (mute | tmp)); -/*---------------------------------------------------------------------------*/ - igot = read_vt(pusb_device, 0x0010); - if (0 > igot) { - SAY("ERROR: failed to read VT1612A register 0x10\n"); - mute = 0x0000; - } else - mute = 0x8000 & ((unsigned int)igot); - mute = 0; - - JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x10,...0x18\n", - mute | tmp | (tmp << 8)); - write_vt(pusb_device, 0x0010, (mute | tmp | (tmp << 8))); - write_vt(pusb_device, 0x0012, (mute | tmp | (tmp << 8))); - write_vt(pusb_device, 0x0014, (mute | tmp | (tmp << 8))); - write_vt(pusb_device, 0x0016, (mute | tmp | (tmp << 8))); - write_vt(pusb_device, 0x0018, (mute | tmp | (tmp << 8))); -/*---------------------------------------------------------------------------*/ - igot = read_vt(pusb_device, 0x001C); - if (0 > igot) { - SAY("ERROR: failed to read VT1612A register 0x1C\n"); - mute = 0x0000; - } else - mute = 0x8000 & ((unsigned int)igot); - mute = 0; - - if (16 <= loud) - tmp = 0x000F & (u8)(loud - 16); - else - tmp = 0; - - JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x1C\n", - mute | tmp | (tmp << 8)); - write_vt(pusb_device, 0x001C, (mute | tmp | (tmp << 8))); - write_vt(pusb_device, 0x001A, 0x0404); - write_vt(pusb_device, 0x0002, 0x0000); - return 0; -} -/*****************************************************************************/ -int audio_gainget(struct usb_device *pusb_device) -{ - int igot; - - if (!pusb_device) - return -ENODEV; - igot = read_vt(pusb_device, 0x001C); - if (0 > igot) - SAY("ERROR: failed to read VT1612A register 0x1C\n"); - return igot; -} -/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_main.c b/drivers/staging/easycap/easycap_main.c deleted file mode 100644 index a45c0b507067..000000000000 --- a/drivers/staging/easycap/easycap_main.c +++ /dev/null @@ -1,4253 +0,0 @@ -/****************************************************************************** -* * -* easycap_main.c * -* * -* Video driver for EasyCAP USB2.0 Video Capture Device DC60 * -* * -* * -******************************************************************************/ -/* - * - * Copyright (C) 2010 R.M. Thomas - * - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ -/*****************************************************************************/ - -#include "easycap.h" -#include - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("R.M. Thomas "); -MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION); -MODULE_VERSION(EASYCAP_DRIVER_VERSION); - -#ifdef CONFIG_EASYCAP_DEBUG -int easycap_debug; -module_param_named(debug, easycap_debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug level: 0(default),1,2,...,9"); -#endif /* CONFIG_EASYCAP_DEBUG */ - -bool easycap_readback; -module_param_named(readback, easycap_readback, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(readback, "read back written registers: (default false)"); - -static int easycap_bars = 1; -module_param_named(bars, easycap_bars, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(bars, - "Testcard bars on input signal failure: 0=>no, 1=>yes(default)"); - -static int easycap_gain = 16; -module_param_named(gain, easycap_gain, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(gain, "Audio gain: 0,...,16(default),...31"); - -static bool easycap_ntsc; -module_param_named(ntsc, easycap_ntsc, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(ntsc, "NTSC default encoding (default PAL)"); - - - -struct easycap_dongle easycapdc60_dongle[DONGLE_MANY]; -static struct mutex mutex_dongle; -static void easycap_complete(struct urb *purb); -static int reset(struct easycap *peasycap); - -const char *strerror(int err) -{ -#define ERRNOSTR(_e) case _e: return # _e - switch (err) { - case 0: return "OK"; - ERRNOSTR(ENOMEM); - ERRNOSTR(ENODEV); - ERRNOSTR(ENXIO); - ERRNOSTR(EINVAL); - ERRNOSTR(EAGAIN); - ERRNOSTR(EFBIG); - ERRNOSTR(EPIPE); - ERRNOSTR(EMSGSIZE); - ERRNOSTR(ENOSPC); - ERRNOSTR(EINPROGRESS); - ERRNOSTR(ENOSR); - ERRNOSTR(EOVERFLOW); - ERRNOSTR(EPROTO); - ERRNOSTR(EILSEQ); - ERRNOSTR(ETIMEDOUT); - ERRNOSTR(EOPNOTSUPP); - ERRNOSTR(EPFNOSUPPORT); - ERRNOSTR(EAFNOSUPPORT); - ERRNOSTR(EADDRINUSE); - ERRNOSTR(EADDRNOTAVAIL); - ERRNOSTR(ENOBUFS); - ERRNOSTR(EISCONN); - ERRNOSTR(ENOTCONN); - ERRNOSTR(ESHUTDOWN); - ERRNOSTR(ENOENT); - ERRNOSTR(ECONNRESET); - ERRNOSTR(ETIME); - ERRNOSTR(ECOMM); - ERRNOSTR(EREMOTEIO); - ERRNOSTR(EXDEV); - ERRNOSTR(EPERM); - default: return "unknown"; - } - -#undef ERRNOSTR -} - -/*---------------------------------------------------------------------------*/ -/* - * PARAMETERS USED WHEN REGISTERING THE VIDEO INTERFACE - * - * NOTE: SOME KERNELS IGNORE usb_class_driver.minor_base, AS MENTIONED BY - * CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGE 253. - * THIS IS THE CASE FOR OpenSUSE. - */ -/*---------------------------------------------------------------------------*/ -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * THIS ROUTINE DOES NOT DETECT DUPLICATE OCCURRENCES OF POINTER peasycap -*/ -/*---------------------------------------------------------------------------*/ -int isdongle(struct easycap *peasycap) -{ - int k; - if (!peasycap) - return -2; - for (k = 0; k < DONGLE_MANY; k++) { - if (easycapdc60_dongle[k].peasycap == peasycap) { - peasycap->isdongle = k; - return k; - } - } - return -1; -} -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -static int easycap_open(struct inode *inode, struct file *file) -{ - struct video_device *pvideo_device; - struct easycap *peasycap; - int rc; - - JOT(4, "\n"); - SAY("==========OPEN=========\n"); - - pvideo_device = video_devdata(file); - if (!pvideo_device) { - SAY("ERROR: pvideo_device is NULL.\n"); - return -EFAULT; - } - peasycap = (struct easycap *)video_get_drvdata(pvideo_device); - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } else { - JOM(16, "peasycap->pusb_device=%p\n", peasycap->pusb_device); - } - file->private_data = peasycap; - rc = wakeup_device(peasycap->pusb_device); - if (0 == rc) - JOM(8, "wakeup_device() OK\n"); - else { - SAM("ERROR: wakeup_device() rc = %i\n", rc); - if (-ENODEV == rc) - SAM("ERROR: wakeup_device() returned -ENODEV\n"); - else - SAM("ERROR: wakeup_device() rc = %i\n", rc); - return rc; - } - peasycap->input = 0; - rc = reset(peasycap); - if (rc) { - SAM("ERROR: reset() rc = %i\n", rc); - return -EFAULT; - } - return 0; -} - -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * RESET THE HARDWARE TO ITS REFERENCE STATE. - * - * THIS ROUTINE MAY BE CALLED REPEATEDLY IF easycap_complete() DETECTS - * A BAD VIDEO FRAME SIZE. -*/ -/*---------------------------------------------------------------------------*/ -static int reset(struct easycap *peasycap) -{ - struct easycap_standard const *peasycap_standard; - int fmtidx, input, rate; - bool ntsc, other; - int rc; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - input = peasycap->input; - -/*---------------------------------------------------------------------------*/ -/* - * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED - * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR - * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE - * A SWITCH BETWEEN PAL AND NTSC. - * - * FUNCTION ready_saa() MAY REQUIRE A SUBSTANTIAL FRACTION OF A SECOND TO - * COMPLETE, SO SHOULD NOT BE INVOKED WITHOUT GOOD REASON. -*/ -/*---------------------------------------------------------------------------*/ - other = false; - JOM(8, "peasycap->ntsc=%d\n", peasycap->ntsc); - - rate = ready_saa(peasycap->pusb_device); - if (rate < 0) { - JOM(8, "not ready to capture after %i ms ...\n", PATIENCE); - ntsc = !peasycap->ntsc; - JOM(8, "... trying %s ..\n", ntsc ? "NTSC" : "PAL"); - rc = setup_stk(peasycap->pusb_device, ntsc); - if (rc) { - SAM("ERROR: setup_stk() rc = %i\n", rc); - return -EFAULT; - } - rc = setup_saa(peasycap->pusb_device, ntsc); - if (rc) { - SAM("ERROR: setup_saa() rc = %i\n", rc); - return -EFAULT; - } - - rate = ready_saa(peasycap->pusb_device); - if (rate < 0) { - JOM(8, "not ready to capture after %i ms\n", PATIENCE); - JOM(8, "... saa register 0x1F has 0x%02X\n", - read_saa(peasycap->pusb_device, 0x1F)); - ntsc = peasycap->ntsc; - } else { - JOM(8, "... success at second try: %i=rate\n", rate); - ntsc = (0 < (rate/2)) ? true : false ; - other = true; - } - } else { - JOM(8, "... success at first try: %i=rate\n", rate); - ntsc = (0 < rate/2) ? true : false ; - } - JOM(8, "ntsc=%d\n", ntsc); -/*---------------------------------------------------------------------------*/ - - rc = setup_stk(peasycap->pusb_device, ntsc); - if (rc) { - SAM("ERROR: setup_stk() rc = %i\n", rc); - return -EFAULT; - } - rc = setup_saa(peasycap->pusb_device, ntsc); - if (rc) { - SAM("ERROR: setup_saa() rc = %i\n", rc); - return -EFAULT; - } - - memset(peasycap->merit, 0, sizeof(peasycap->merit)); - - peasycap->video_eof = 0; - peasycap->audio_eof = 0; -/*---------------------------------------------------------------------------*/ -/* - * RESTORE INPUT AND FORCE REFRESH OF STANDARD, FORMAT, ETC. - * - * WHILE THIS PROCEDURE IS IN PROGRESS, SOME IOCTL COMMANDS WILL RETURN -EBUSY. -*/ -/*---------------------------------------------------------------------------*/ - peasycap->input = -8192; - peasycap->standard_offset = -8192; - fmtidx = ntsc ? NTSC_M : PAL_BGHIN; - if (other) { - peasycap_standard = &easycap_standard[0]; - while (0xFFFF != peasycap_standard->mask) { - if (fmtidx == peasycap_standard->v4l2_standard.index) { - peasycap->inputset[input].standard_offset = - peasycap_standard - easycap_standard; - break; - } - peasycap_standard++; - } - if (0xFFFF == peasycap_standard->mask) { - SAM("ERROR: standard not found\n"); - return -EINVAL; - } - JOM(8, "%i=peasycap->inputset[%i].standard_offset\n", - peasycap->inputset[input].standard_offset, input); - } - peasycap->format_offset = -8192; - peasycap->brightness = -8192; - peasycap->contrast = -8192; - peasycap->saturation = -8192; - peasycap->hue = -8192; - - rc = newinput(peasycap, input); - - if (rc) { - SAM("ERROR: newinput(.,%i) rc = %i\n", rc, input); - return -EFAULT; - } - JOM(4, "restored input, standard and format\n"); - - JOM(8, "true=peasycap->ntsc %d\n", peasycap->ntsc); - - if (0 > peasycap->input) { - SAM("MISTAKE: %i=peasycap->input\n", peasycap->input); - return -ENOENT; - } - if (0 > peasycap->standard_offset) { - SAM("MISTAKE: %i=peasycap->standard_offset\n", - peasycap->standard_offset); - return -ENOENT; - } - if (0 > peasycap->format_offset) { - SAM("MISTAKE: %i=peasycap->format_offset\n", - peasycap->format_offset); - return -ENOENT; - } - if (0 > peasycap->brightness) { - SAM("MISTAKE: %i=peasycap->brightness\n", - peasycap->brightness); - return -ENOENT; - } - if (0 > peasycap->contrast) { - SAM("MISTAKE: %i=peasycap->contrast\n", peasycap->contrast); - return -ENOENT; - } - if (0 > peasycap->saturation) { - SAM("MISTAKE: %i=peasycap->saturation\n", - peasycap->saturation); - return -ENOENT; - } - if (0 > peasycap->hue) { - SAM("MISTAKE: %i=peasycap->hue\n", peasycap->hue); - return -ENOENT; - } - return 0; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * IF THE REQUESTED INPUT IS THE SAME AS THE EXISTING INPUT, DO NOTHING. - * OTHERWISE: - * KILL URBS, CLEAR FIELD AND FRAME BUFFERS AND RESET THEIR - * _read AND _fill POINTERS. - * SELECT THE NEW INPUT. - * ADJUST THE STANDARD, FORMAT, BRIGHTNESS, CONTRAST, SATURATION AND HUE - * ON THE BASIS OF INFORMATION IN STRUCTURE easycap.inputset[input]. - * RESUBMIT THE URBS IF STREAMING WAS ALREADY IN PROGRESS. - * - * NOTE: - * THIS ROUTINE MAY BE CALLED FREQUENTLY BY ZONEMINDER VIA IOCTL, - * SO IT SHOULD WRITE ONLY SPARINGLY TO THE LOGFILE. -*/ -/*---------------------------------------------------------------------------*/ -int -newinput(struct easycap *peasycap, int input) -{ - int rc, k, m, mood, off; - int inputnow, video_idlenow, audio_idlenow; - bool resubmit; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - JOM(8, "%i=input sought\n", input); - - if (0 > input && INPUT_MANY <= input) - return -ENOENT; - inputnow = peasycap->input; - if (input == inputnow) - return 0; -/*---------------------------------------------------------------------------*/ -/* - * IF STREAMING IS IN PROGRESS THE URBS ARE KILLED AT THIS - * STAGE AND WILL BE RESUBMITTED PRIOR TO EXIT FROM THE ROUTINE. - * IF NO STREAMING IS IN PROGRESS NO URBS WILL BE SUBMITTED BY THE - * ROUTINE. -*/ -/*---------------------------------------------------------------------------*/ - video_idlenow = peasycap->video_idle; - audio_idlenow = peasycap->audio_idle; - - peasycap->video_idle = 1; - peasycap->audio_idle = 1; - if (peasycap->video_isoc_streaming) { - resubmit = true; - kill_video_urbs(peasycap); - } else { - resubmit = false; - } -/*---------------------------------------------------------------------------*/ - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -ENODEV; - } - rc = usb_set_interface(peasycap->pusb_device, - peasycap->video_interface, - peasycap->video_altsetting_off); - if (rc) { - SAM("ERROR: usb_set_interface() rc = %i\n", rc); - return -EFAULT; - } - rc = stop_100(peasycap->pusb_device); - if (rc) { - SAM("ERROR: stop_100() rc = %i\n", rc); - return -EFAULT; - } - for (k = 0; k < FIELD_BUFFER_MANY; k++) { - for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) - memset(peasycap->field_buffer[k][m].pgo, 0, PAGE_SIZE); - } - for (k = 0; k < FRAME_BUFFER_MANY; k++) { - for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) - memset(peasycap->frame_buffer[k][m].pgo, 0, PAGE_SIZE); - } - peasycap->field_page = 0; - peasycap->field_read = 0; - peasycap->field_fill = 0; - - peasycap->frame_read = 0; - peasycap->frame_fill = 0; - for (k = 0; k < peasycap->input; k++) { - (peasycap->frame_fill)++; - if (peasycap->frame_buffer_many <= peasycap->frame_fill) - peasycap->frame_fill = 0; - } - peasycap->input = input; - select_input(peasycap->pusb_device, peasycap->input, 9); -/*---------------------------------------------------------------------------*/ - if (input == peasycap->inputset[input].input) { - off = peasycap->inputset[input].standard_offset; - if (off != peasycap->standard_offset) { - rc = adjust_standard(peasycap, - easycap_standard[off].v4l2_standard.id); - if (rc) { - SAM("ERROR: adjust_standard() rc = %i\n", rc); - return -EFAULT; - } - JOM(8, "%i=peasycap->standard_offset\n", - peasycap->standard_offset); - } else { - JOM(8, "%i=peasycap->standard_offset unchanged\n", - peasycap->standard_offset); - } - off = peasycap->inputset[input].format_offset; - if (off != peasycap->format_offset) { - struct v4l2_pix_format *pix = - &easycap_format[off].v4l2_format.fmt.pix; - rc = adjust_format(peasycap, - pix->width, pix->height, - pix->pixelformat, pix->field, false); - if (0 > rc) { - SAM("ERROR: adjust_format() rc = %i\n", rc); - return -EFAULT; - } - JOM(8, "%i=peasycap->format_offset\n", - peasycap->format_offset); - } else { - JOM(8, "%i=peasycap->format_offset unchanged\n", - peasycap->format_offset); - } - mood = peasycap->inputset[input].brightness; - if (mood != peasycap->brightness) { - rc = adjust_brightness(peasycap, mood); - if (rc) { - SAM("ERROR: adjust_brightness rc = %i\n", rc); - return -EFAULT; - } - JOM(8, "%i=peasycap->brightness\n", - peasycap->brightness); - } - mood = peasycap->inputset[input].contrast; - if (mood != peasycap->contrast) { - rc = adjust_contrast(peasycap, mood); - if (rc) { - SAM("ERROR: adjust_contrast rc = %i\n", rc); - return -EFAULT; - } - JOM(8, "%i=peasycap->contrast\n", peasycap->contrast); - } - mood = peasycap->inputset[input].saturation; - if (mood != peasycap->saturation) { - rc = adjust_saturation(peasycap, mood); - if (rc) { - SAM("ERROR: adjust_saturation rc = %i\n", rc); - return -EFAULT; - } - JOM(8, "%i=peasycap->saturation\n", - peasycap->saturation); - } - mood = peasycap->inputset[input].hue; - if (mood != peasycap->hue) { - rc = adjust_hue(peasycap, mood); - if (rc) { - SAM("ERROR: adjust_hue rc = %i\n", rc); - return -EFAULT; - } - JOM(8, "%i=peasycap->hue\n", peasycap->hue); - } - } else { - SAM("MISTAKE: easycap.inputset[%i] unpopulated\n", input); - return -ENOENT; - } -/*---------------------------------------------------------------------------*/ - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -ENODEV; - } - rc = usb_set_interface(peasycap->pusb_device, - peasycap->video_interface, - peasycap->video_altsetting_on); - if (rc) { - SAM("ERROR: usb_set_interface() rc = %i\n", rc); - return -EFAULT; - } - rc = start_100(peasycap->pusb_device); - if (rc) { - SAM("ERROR: start_100() rc = %i\n", rc); - return -EFAULT; - } - if (resubmit) - submit_video_urbs(peasycap); - - peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1; - peasycap->video_idle = video_idlenow; - peasycap->audio_idle = audio_idlenow; - peasycap->video_junk = 0; - - return 0; -} -/*****************************************************************************/ -int submit_video_urbs(struct easycap *peasycap) -{ - struct data_urb *pdata_urb; - struct urb *purb; - struct list_head *plist_head; - int j, isbad, nospc, m, rc; - int isbuf; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - - if (!peasycap->purb_video_head) { - SAY("ERROR: peasycap->urb_video_head uninitialized\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAY("ERROR: peasycap->pusb_device is NULL\n"); - return -ENODEV; - } - if (!peasycap->video_isoc_streaming) { - JOM(4, "submission of all video urbs\n"); - isbad = 0; nospc = 0; m = 0; - list_for_each(plist_head, (peasycap->purb_video_head)) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - purb = pdata_urb->purb; - isbuf = pdata_urb->isbuf; - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = - usb_rcvisocpipe(peasycap->pusb_device, - peasycap->video_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = - peasycap->video_isoc_buffer[isbuf].pgo; - purb->transfer_buffer_length = - peasycap->video_isoc_buffer_size; - purb->complete = easycap_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = - peasycap->video_isoc_framesperdesc; - - for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j]. offset = - j * peasycap->video_isoc_maxframesize; - purb->iso_frame_desc[j]. length = - peasycap->video_isoc_maxframesize; - } - - rc = usb_submit_urb(purb, GFP_KERNEL); - if (rc) { - isbad++; - SAM("ERROR: usb_submit_urb() failed " - "for urb with rc:-%s\n", - strerror(rc)); - if (rc == -ENOSPC) - nospc++; - } else { - m++; - } - } else { - isbad++; - } - } - if (nospc) { - SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc); - SAM("..... possibly inadequate USB bandwidth\n"); - peasycap->video_eof = 1; - } - - if (isbad) { - JOM(4, "attempting cleanup instead of submitting\n"); - list_for_each(plist_head, (peasycap->purb_video_head)) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - purb = pdata_urb->purb; - if (purb) - usb_kill_urb(purb); - } - } - peasycap->video_isoc_streaming = 0; - } else { - peasycap->video_isoc_streaming = 1; - JOM(4, "submitted %i video urbs\n", m); - } - } else { - JOM(4, "already streaming video urbs\n"); - } - return 0; -} -/*****************************************************************************/ -int kill_video_urbs(struct easycap *peasycap) -{ - int m; - struct list_head *plist_head; - struct data_urb *pdata_urb; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->video_isoc_streaming) { - JOM(8, "%i=video_isoc_streaming, no video urbs killed\n", - peasycap->video_isoc_streaming); - return 0; - } - if (!peasycap->purb_video_head) { - SAM("ERROR: peasycap->purb_video_head is NULL\n"); - return -EFAULT; - } - - peasycap->video_isoc_streaming = 0; - JOM(4, "killing video urbs\n"); - m = 0; - list_for_each(plist_head, (peasycap->purb_video_head)) { - pdata_urb = list_entry(plist_head, struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - usb_kill_urb(pdata_urb->purb); - m++; - } - } - JOM(4, "%i video urbs killed\n", m); - - return 0; -} -/****************************************************************************/ -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*--------------------------------------------------------------------------*/ -static int easycap_open_noinode(struct file *file) -{ - return easycap_open(NULL, file); -} - -static int videodev_release(struct video_device *pvideo_device) -{ - struct easycap *peasycap; - - peasycap = video_get_drvdata(pvideo_device); - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - SAY("ending unsuccessfully\n"); - return -EFAULT; - } - if (0 != kill_video_urbs(peasycap)) { - SAM("ERROR: kill_video_urbs() failed\n"); - return -EFAULT; - } - JOM(4, "ending successfully\n"); - return 0; -} -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ -/*****************************************************************************/ -/*--------------------------------------------------------------------------*/ -/* - * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS - * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect(). - * - * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO - * peasycap->pusb_device IS NO LONGER VALID. - */ -/*---------------------------------------------------------------------------*/ -static void easycap_delete(struct kref *pkref) -{ - struct easycap *peasycap; - struct data_urb *pdata_urb; - struct list_head *plist_head, *plist_next; - int k, m, gone, kd; - int allocation_video_urb; - int allocation_video_page; - int allocation_video_struct; - int allocation_audio_urb; - int allocation_audio_page; - int allocation_audio_struct; - int registered_video, registered_audio; - - peasycap = container_of(pkref, struct easycap, kref); - if (!peasycap) { - SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); - return; - } - kd = isdongle(peasycap); -/*---------------------------------------------------------------------------*/ -/* - * FREE VIDEO. - */ -/*---------------------------------------------------------------------------*/ - if (peasycap->purb_video_head) { - JOM(4, "freeing video urbs\n"); - m = 0; - list_for_each(plist_head, (peasycap->purb_video_head)) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (!pdata_urb) { - JOM(4, "ERROR: pdata_urb is NULL\n"); - } else { - if (pdata_urb->purb) { - usb_free_urb(pdata_urb->purb); - pdata_urb->purb = NULL; - peasycap->allocation_video_urb -= 1; - m++; - } - } - } - - JOM(4, "%i video urbs freed\n", m); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video data_urb structures.\n"); - m = 0; - list_for_each_safe(plist_head, plist_next, - peasycap->purb_video_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - peasycap->allocation_video_struct -= - sizeof(struct data_urb); - kfree(pdata_urb); - pdata_urb = NULL; - m++; - } - } - JOM(4, "%i video data_urb structures freed\n", m); - JOM(4, "setting peasycap->purb_video_head=NULL\n"); - peasycap->purb_video_head = NULL; - } -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video isoc buffers.\n"); - m = 0; - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - if (peasycap->video_isoc_buffer[k].pgo) { - free_pages((unsigned long) - peasycap->video_isoc_buffer[k].pgo, - VIDEO_ISOC_ORDER); - peasycap->video_isoc_buffer[k].pgo = NULL; - peasycap->allocation_video_page -= - BIT(VIDEO_ISOC_ORDER); - m++; - } - } - JOM(4, "isoc video buffers freed: %i pages\n", - m * (0x01 << VIDEO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video field buffers.\n"); - gone = 0; - for (k = 0; k < FIELD_BUFFER_MANY; k++) { - for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->field_buffer[k][m].pgo) { - free_page((unsigned long) - peasycap->field_buffer[k][m].pgo); - peasycap->field_buffer[k][m].pgo = NULL; - peasycap->allocation_video_page -= 1; - gone++; - } - } - } - JOM(4, "video field buffers freed: %i pages\n", gone); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing video frame buffers.\n"); - gone = 0; - for (k = 0; k < FRAME_BUFFER_MANY; k++) { - for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->frame_buffer[k][m].pgo) { - free_page((unsigned long) - peasycap->frame_buffer[k][m].pgo); - peasycap->frame_buffer[k][m].pgo = NULL; - peasycap->allocation_video_page -= 1; - gone++; - } - } - } - JOM(4, "video frame buffers freed: %i pages\n", gone); -/*---------------------------------------------------------------------------*/ -/* - * FREE AUDIO. - */ -/*---------------------------------------------------------------------------*/ - if (peasycap->purb_audio_head) { - JOM(4, "freeing audio urbs\n"); - m = 0; - list_for_each(plist_head, (peasycap->purb_audio_head)) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (!pdata_urb) - JOM(4, "ERROR: pdata_urb is NULL\n"); - else { - if (pdata_urb->purb) { - usb_free_urb(pdata_urb->purb); - pdata_urb->purb = NULL; - peasycap->allocation_audio_urb -= 1; - m++; - } - } - } - JOM(4, "%i audio urbs freed\n", m); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing audio data_urb structures.\n"); - m = 0; - list_for_each_safe(plist_head, plist_next, - peasycap->purb_audio_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - peasycap->allocation_audio_struct -= - sizeof(struct data_urb); - kfree(pdata_urb); - pdata_urb = NULL; - m++; - } - } - JOM(4, "%i audio data_urb structures freed\n", m); - JOM(4, "setting peasycap->purb_audio_head=NULL\n"); - peasycap->purb_audio_head = NULL; - } -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing audio isoc buffers.\n"); - m = 0; - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - if (peasycap->audio_isoc_buffer[k].pgo) { - free_pages((unsigned long) - (peasycap->audio_isoc_buffer[k].pgo), - AUDIO_ISOC_ORDER); - peasycap->audio_isoc_buffer[k].pgo = NULL; - peasycap->allocation_audio_page -= - BIT(AUDIO_ISOC_ORDER); - m++; - } - } - JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", - m * (0x01 << AUDIO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ - JOM(4, "freeing easycap structure.\n"); - allocation_video_urb = peasycap->allocation_video_urb; - allocation_video_page = peasycap->allocation_video_page; - allocation_video_struct = peasycap->allocation_video_struct; - registered_video = peasycap->registered_video; - allocation_audio_urb = peasycap->allocation_audio_urb; - allocation_audio_page = peasycap->allocation_audio_page; - allocation_audio_struct = peasycap->allocation_audio_struct; - registered_audio = peasycap->registered_audio; - - if (0 <= kd && DONGLE_MANY > kd) { - if (mutex_lock_interruptible(&mutex_dongle)) { - SAY("ERROR: cannot down mutex_dongle\n"); - } else { - JOM(4, "locked mutex_dongle\n"); - easycapdc60_dongle[kd].peasycap = NULL; - mutex_unlock(&mutex_dongle); - JOM(4, "unlocked mutex_dongle\n"); - JOT(4, " null-->dongle[%i].peasycap\n", kd); - allocation_video_struct -= sizeof(struct easycap); - } - } else { - SAY("ERROR: cannot purge dongle[].peasycap"); - } - - kfree(peasycap); - -/*---------------------------------------------------------------------------*/ - SAY("%8i=video urbs after all deletions\n", allocation_video_urb); - SAY("%8i=video pages after all deletions\n", allocation_video_page); - SAY("%8i=video structs after all deletions\n", allocation_video_struct); - SAY("%8i=video devices after all deletions\n", registered_video); - SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb); - SAY("%8i=audio pages after all deletions\n", allocation_audio_page); - SAY("%8i=audio structs after all deletions\n", allocation_audio_struct); - SAY("%8i=audio devices after all deletions\n", registered_audio); - - JOT(4, "ending.\n"); - return; -} -/*****************************************************************************/ -static unsigned int easycap_poll(struct file *file, poll_table *wait) -{ - struct easycap *peasycap; - int rc, kd; - - JOT(8, "\n"); - - if (NULL == ((poll_table *)wait)) - JOT(8, "WARNING: poll table pointer is NULL ... continuing\n"); - if (!file) { - SAY("ERROR: file pointer is NULL\n"); - return -ERESTARTSYS; - } - peasycap = file->private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAY("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } -/*---------------------------------------------------------------------------*/ - kd = isdongle(peasycap); - if (0 <= kd && DONGLE_MANY > kd) { - if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { - SAY("ERROR: cannot down dongle[%i].mutex_video\n", kd); - return -ERESTARTSYS; - } - JOM(4, "locked dongle[%i].mutex_video\n", kd); - /* - * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER - * peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL. - * IF NECESSARY, BAIL OUT. - */ - if (kd != isdongle(peasycap)) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ERESTARTSYS; - } - if (!file) { - SAY("ERROR: file is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ERESTARTSYS; - } - peasycap = file->private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ERESTARTSYS; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return -ERESTARTSYS; - } - } else - /* - * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap - * BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL - * HAVE FAILED. BAIL OUT. - */ - return -ERESTARTSYS; -/*---------------------------------------------------------------------------*/ - rc = easycap_dqbuf(peasycap, 0); - peasycap->polled = 1; - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - if (0 == rc) - return POLLIN | POLLRDNORM; - else - return POLLERR; - } -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * IF mode IS NONZERO THIS ROUTINE RETURNS -EAGAIN RATHER THAN BLOCKING. - */ -/*---------------------------------------------------------------------------*/ -int easycap_dqbuf(struct easycap *peasycap, int mode) -{ - int input, ifield, miss, rc; - - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAY("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - ifield = 0; - JOM(8, "%i=ifield\n", ifield); -/*---------------------------------------------------------------------------*/ -/* - * CHECK FOR LOST INPUT SIGNAL. - * - * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED. - * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT - * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE - * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS: - * - * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK - * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK - * INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK - * INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS -*/ -/*---------------------------------------------------------------------------*/ - input = peasycap->input; - if (0 <= input && INPUT_MANY > input) { - rc = read_saa(peasycap->pusb_device, 0x1F); - if (0 <= rc) { - if (rc & 0x40) - peasycap->lost[input] += 1; - else - peasycap->lost[input] -= 2; - - if (0 > peasycap->lost[input]) - peasycap->lost[input] = 0; - else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input]) - peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE); - } - } -/*---------------------------------------------------------------------------*/ -/* - * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM) - */ -/*---------------------------------------------------------------------------*/ - miss = 0; - while ((peasycap->field_read == peasycap->field_fill) || - (0 != (0xFF00 & peasycap->field_buffer - [peasycap->field_read][0].kount)) || - (ifield != (0x00FF & peasycap->field_buffer - [peasycap->field_read][0].kount))) { - if (mode) - return -EAGAIN; - - JOM(8, "first wait on wq_video, %i=field_read %i=field_fill\n", - peasycap->field_read, peasycap->field_fill); - - if (0 != (wait_event_interruptible(peasycap->wq_video, - (peasycap->video_idle || peasycap->video_eof || - ((peasycap->field_read != peasycap->field_fill) && - (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) && - (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) { - SAM("aborted by signal\n"); - return -EIO; - } - if (peasycap->video_idle) { - JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n", - peasycap->video_idle); - return -EAGAIN; - } - if (peasycap->video_eof) { - JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof); - #if defined(PERSEVERE) - if (1 == peasycap->status) { - JOM(8, "persevering ...\n"); - peasycap->video_eof = 0; - peasycap->audio_eof = 0; - if (0 != reset(peasycap)) { - JOM(8, " ... failed returning -EIO\n"); - peasycap->video_eof = 1; - peasycap->audio_eof = 1; - kill_video_urbs(peasycap); - return -EIO; - } - peasycap->status = 0; - JOM(8, " ... OK returning -EAGAIN\n"); - return -EAGAIN; - } - #endif /*PERSEVERE*/ - peasycap->video_eof = 1; - peasycap->audio_eof = 1; - kill_video_urbs(peasycap); - JOM(8, "returning -EIO\n"); - return -EIO; - } - miss++; - } - JOM(8, "first awakening on wq_video after %i waits\n", miss); - - rc = field2frame(peasycap); - if (rc) - SAM("ERROR: field2frame() rc = %i\n", rc); -/*---------------------------------------------------------------------------*/ -/* - * WAIT FOR THE OTHER FIELD - */ -/*---------------------------------------------------------------------------*/ - if (ifield) - ifield = 0; - else - ifield = 1; - miss = 0; - while ((peasycap->field_read == peasycap->field_fill) || - (0 != (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) || - (ifield != (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))) { - if (mode) - return -EAGAIN; - - JOM(8, "second wait on wq_video %i=field_read %i=field_fill\n", - peasycap->field_read, peasycap->field_fill); - if (0 != (wait_event_interruptible(peasycap->wq_video, - (peasycap->video_idle || peasycap->video_eof || - ((peasycap->field_read != peasycap->field_fill) && - (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) && - (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) { - SAM("aborted by signal\n"); - return -EIO; - } - if (peasycap->video_idle) { - JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n", - peasycap->video_idle); - return -EAGAIN; - } - if (peasycap->video_eof) { - JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof); -#if defined(PERSEVERE) - if (1 == peasycap->status) { - JOM(8, "persevering ...\n"); - peasycap->video_eof = 0; - peasycap->audio_eof = 0; - if (0 != reset(peasycap)) { - JOM(8, " ... failed returning -EIO\n"); - peasycap->video_eof = 1; - peasycap->audio_eof = 1; - kill_video_urbs(peasycap); - return -EIO; - } - peasycap->status = 0; - JOM(8, " ... OK ... returning -EAGAIN\n"); - return -EAGAIN; - } -#endif /*PERSEVERE*/ - peasycap->video_eof = 1; - peasycap->audio_eof = 1; - kill_video_urbs(peasycap); - JOM(8, "returning -EIO\n"); - return -EIO; - } - miss++; - } - JOM(8, "second awakening on wq_video after %i waits\n", miss); - - rc = field2frame(peasycap); - if (rc) - SAM("ERROR: field2frame() rc = %i\n", rc); -/*---------------------------------------------------------------------------*/ -/* - * WASTE THIS FRAME -*/ -/*---------------------------------------------------------------------------*/ - if (peasycap->skip) { - peasycap->skipped++; - if (peasycap->skip != peasycap->skipped) - return peasycap->skip - peasycap->skipped; - else - peasycap->skipped = 0; - } -/*---------------------------------------------------------------------------*/ - peasycap->frame_read = peasycap->frame_fill; - peasycap->queued[peasycap->frame_read] = 0; - peasycap->done[peasycap->frame_read] = V4L2_BUF_FLAG_DONE; - - peasycap->frame_fill++; - if (peasycap->frame_buffer_many <= peasycap->frame_fill) - peasycap->frame_fill = 0; - - if (0x01 & easycap_standard[peasycap->standard_offset].mask) - peasycap->frame_buffer[peasycap->frame_read][0].kount = - V4L2_FIELD_TOP; - else - peasycap->frame_buffer[peasycap->frame_read][0].kount = - V4L2_FIELD_BOTTOM; - - - JOM(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read); - JOM(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill); - - return 0; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * BY DEFINITION, odd IS true FOR THE FIELD OCCUPYING LINES 1,3,5,...,479 - * odd IS false FOR THE FIELD OCCUPYING LINES 0,2,4,...,478 - * - * WHEN BOOLEAN PARAMETER decimatepixel IS true, ONLY THE FIELD FOR WHICH - * odd==false IS TRANSFERRED TO THE FRAME BUFFER. - * - * THE BOOLEAN PARAMETER offerfields IS true ONLY WHEN THE USER PROGRAM - * CHOOSES THE OPTION V4L2_FIELD_INTERLACED. - */ -/*---------------------------------------------------------------------------*/ -int -field2frame(struct easycap *peasycap) -{ - - void *pex, *pad; - int kex, kad, mex, mad, rex, rad, rad2; - int c2, c3, w2, w3, cz, wz; - int rc, bytesperpixel, multiplier; - int much, more, over, rump, caches, input; - u8 mask, margin; - bool odd, isuy, decimatepixel, offerfields, badinput; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - - badinput = false; - input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input; - - JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> " - "frame buffer %i\n", - peasycap->field_buffer[peasycap->field_read][0].kount, - peasycap->field_buffer[peasycap->field_read][0].input, - peasycap->field_read, peasycap->frame_fill); - JOM(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel); - if (peasycap->offerfields) - JOM(8, "===== offerfields\n"); - -/*---------------------------------------------------------------------------*/ -/* - * REJECT OR CLEAN BAD FIELDS - */ -/*---------------------------------------------------------------------------*/ - if (peasycap->field_read == peasycap->field_fill) { - SAM("ERROR: on entry, still filling field buffer %i\n", - peasycap->field_read); - return 0; - } -#ifdef EASYCAP_TESTCARD - easycap_testcard(peasycap, peasycap->field_read); -#else - if (0 <= input && INPUT_MANY > input) { - if (easycap_bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input]) - easycap_testcard(peasycap, peasycap->field_read); - } -#endif /*EASYCAP_TESTCARD*/ -/*---------------------------------------------------------------------------*/ - - offerfields = peasycap->offerfields; - bytesperpixel = peasycap->bytesperpixel; - decimatepixel = peasycap->decimatepixel; - - if ((2 != bytesperpixel) && - (3 != bytesperpixel) && - (4 != bytesperpixel)) { - SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel); - return -EFAULT; - } - if (decimatepixel) - multiplier = 2; - else - multiplier = 1; - - w2 = 2 * multiplier * (peasycap->width); - w3 = bytesperpixel * multiplier * (peasycap->width); - wz = multiplier * (peasycap->height) * - multiplier * (peasycap->width); - - kex = peasycap->field_read; mex = 0; - kad = peasycap->frame_fill; mad = 0; - - pex = peasycap->field_buffer[kex][0].pgo; rex = PAGE_SIZE; - pad = peasycap->frame_buffer[kad][0].pgo; rad = PAGE_SIZE; - odd = !!(peasycap->field_buffer[kex][0].kount); - - if (odd && (!decimatepixel)) { - JOM(8, "initial skipping %4i bytes p.%4i\n", - w3/multiplier, mad); - pad += (w3 / multiplier); rad -= (w3 / multiplier); - } - isuy = true; - mask = 0; rump = 0; caches = 0; - - cz = 0; - while (cz < wz) { - /* - * PROCESS ONE LINE OF FRAME AT FULL RESOLUTION: - * READ w2 BYTES FROM FIELD BUFFER, - * WRITE w3 BYTES TO FRAME BUFFER - */ - if (!decimatepixel) { - over = w2; - do { - much = over; more = 0; - margin = 0; mask = 0x00; - if (rex < much) - much = rex; - rump = 0; - - if (much % 2) { - SAM("MISTAKE: much is odd\n"); - return -EFAULT; - } - - more = (bytesperpixel * - much) / 2; -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - if (1 < bytesperpixel) { - if (rad * 2 < much * bytesperpixel) { - /* - * INJUDICIOUS ALTERATION OF - * THIS STATEMENT BLOCK WILL - * CAUSE BREAKAGE. BEWARE. - */ - rad2 = rad + bytesperpixel - 1; - much = ((((2 * rad2)/bytesperpixel)/2) * 2); - rump = ((bytesperpixel * much) / 2) - rad; - more = rad; - } - mask = (u8)rump; - margin = 0; - if (much == rex) { - mask |= 0x04; - if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE) - margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo)); - else - mask |= 0x08; - } - } else { - SAM("MISTAKE: %i=bytesperpixel\n", - bytesperpixel); - return -EFAULT; - } - if (rump) - caches++; - if (badinput) { - JOM(8, "ERROR: 0x%02X=->field_buffer" - "[%i][%i].input, " - "0x%02X=(0x08|->input)\n", - peasycap->field_buffer - [kex][mex].input, kex, mex, - (0x08|peasycap->input)); - } - rc = redaub(peasycap, pad, pex, much, more, - mask, margin, isuy); - if (0 > rc) { - SAM("ERROR: redaub() failed\n"); - return -EFAULT; - } - if (much % 4) - isuy = !isuy; - - over -= much; cz += much; - pex += much; rex -= much; - if (!rex) { - mex++; - pex = peasycap->field_buffer[kex][mex].pgo; - rex = PAGE_SIZE; - if (peasycap->field_buffer[kex][mex].input != (0x08|peasycap->input)) - badinput = true; - } - pad += more; - rad -= more; - if (!rad) { - mad++; - pad = peasycap->frame_buffer[kad][mad].pgo; - rad = PAGE_SIZE; - if (rump) { - pad += rump; - rad -= rump; - } - } - } while (over); -/*---------------------------------------------------------------------------*/ -/* - * SKIP w3 BYTES IN TARGET FRAME BUFFER, - * UNLESS IT IS THE LAST LINE OF AN ODD FRAME - */ -/*---------------------------------------------------------------------------*/ - if (!odd || (cz != wz)) { - over = w3; - do { - if (!rad) { - mad++; - pad = peasycap->frame_buffer - [kad][mad].pgo; - rad = PAGE_SIZE; - } - more = over; - if (rad < more) - more = rad; - over -= more; - pad += more; - rad -= more; - } while (over); - } -/*---------------------------------------------------------------------------*/ -/* - * PROCESS ONE LINE OF FRAME AT REDUCED RESOLUTION: - * ONLY IF false==odd, - * READ w2 BYTES FROM FIELD BUFFER, - * WRITE w3 / 2 BYTES TO FRAME BUFFER - */ -/*---------------------------------------------------------------------------*/ - } else if (!odd) { - over = w2; - do { - much = over; more = 0; margin = 0; mask = 0x00; - if (rex < much) - much = rex; - rump = 0; - - if (much % 2) { - SAM("MISTAKE: much is odd\n"); - return -EFAULT; - } - - more = (bytesperpixel * much) / 4; -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - if (1 < bytesperpixel) { - if (rad * 4 < much * bytesperpixel) { - /* - * INJUDICIOUS ALTERATION OF - * THIS STATEMENT BLOCK - * WILL CAUSE BREAKAGE. - * BEWARE. - */ - rad2 = rad + bytesperpixel - 1; - much = ((((2 * rad2) / bytesperpixel) / 2) * 4); - rump = ((bytesperpixel * much) / 4) - rad; - more = rad; - } - mask = (u8)rump; - margin = 0; - if (much == rex) { - mask |= 0x04; - if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE) - margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo)); - else - mask |= 0x08; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - } else { - SAM("MISTAKE: %i=bytesperpixel\n", - bytesperpixel); - return -EFAULT; - } -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - if (rump) - caches++; - - if (badinput) { - JOM(8, "ERROR: 0x%02X=->field_buffer" - "[%i][%i].input, " - "0x%02X=(0x08|->input)\n", - peasycap->field_buffer - [kex][mex].input, kex, mex, - (0x08|peasycap->input)); - } - rc = redaub(peasycap, pad, pex, much, more, - mask, margin, isuy); - if (0 > rc) { - SAM("ERROR: redaub() failed\n"); - return -EFAULT; - } - over -= much; cz += much; - pex += much; rex -= much; - if (!rex) { - mex++; - pex = peasycap->field_buffer[kex][mex].pgo; - rex = PAGE_SIZE; - if (peasycap->field_buffer[kex][mex].input != - (0x08|peasycap->input)) - badinput = true; - } - pad += more; - rad -= more; - if (!rad) { - mad++; - pad = peasycap->frame_buffer[kad][mad].pgo; - rad = PAGE_SIZE; - if (rump) { - pad += rump; - rad -= rump; - } - } - } while (over); -/*---------------------------------------------------------------------------*/ -/* - * OTHERWISE JUST - * READ w2 BYTES FROM FIELD BUFFER AND DISCARD THEM - */ -/*---------------------------------------------------------------------------*/ - } else { - over = w2; - do { - if (!rex) { - mex++; - pex = peasycap->field_buffer[kex][mex].pgo; - rex = PAGE_SIZE; - if (peasycap->field_buffer[kex][mex].input != - (0x08|peasycap->input)) { - JOM(8, "ERROR: 0x%02X=->field_buffer" - "[%i][%i].input, " - "0x%02X=(0x08|->input)\n", - peasycap->field_buffer - [kex][mex].input, kex, mex, - (0x08|peasycap->input)); - badinput = true; - } - } - much = over; - if (rex < much) - much = rex; - over -= much; - cz += much; - pex += much; - rex -= much; - } while (over); - } - } -/*---------------------------------------------------------------------------*/ -/* - * SANITY CHECKS - */ -/*---------------------------------------------------------------------------*/ - c2 = (mex + 1)*PAGE_SIZE - rex; - if (cz != c2) - SAM("ERROR: discrepancy %i in bytes read\n", c2 - cz); - c3 = (mad + 1)*PAGE_SIZE - rad; - - if (!decimatepixel) { - if (bytesperpixel * cz != c3) - SAM("ERROR: discrepancy %i in bytes written\n", - c3 - (bytesperpixel * cz)); - } else { - if (!odd) { - if (bytesperpixel * - cz != (4 * c3)) - SAM("ERROR: discrepancy %i in bytes written\n", - (2*c3)-(bytesperpixel * cz)); - } else { - if (0 != c3) - SAM("ERROR: discrepancy %i " - "in bytes written\n", c3); - } - } - if (rump) - SAM("WORRY: undischarged cache at end of line in frame buffer\n"); - - JOM(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3); - JOM(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad); - - if (odd) - JOM(8, "+++++ field2frame(): frame buffer %i is full\n", kad); - - if (peasycap->field_read == peasycap->field_fill) - SAM("WARNING: on exit, filling field buffer %i\n", - peasycap->field_read); - - if (caches) - JOM(8, "%i=caches\n", caches); - return 0; -} -/*---------------------------------------------------------------------------*/ -/* - * DECIMATION AND COLOURSPACE CONVERSION. - * - * THIS ROUTINE REQUIRES THAT ALL THE DATA TO BE READ RESIDES ON ONE PAGE - * AND THAT ALL THE DATA TO BE WRITTEN RESIDES ON ONE (DIFFERENT) PAGE. - * THE CALLING ROUTINE MUST ENSURE THAT THIS REQUIREMENT IS MET, AND MUST - * ALSO ENSURE THAT much IS EVEN. - * - * much BYTES ARE READ, AT LEAST (bytesperpixel * much)/2 BYTES ARE WRITTEN - * IF THERE IS NO DECIMATION, HALF THIS AMOUNT IF THERE IS DECIMATION. - * - * mask IS ZERO WHEN NO SPECIAL BEHAVIOUR REQUIRED. OTHERWISE IT IS SET THUS: - * 0x03 & mask = number of bytes to be written to cache instead of to - * frame buffer - * 0x04 & mask => use argument margin to set the chrominance for last pixel - * 0x08 & mask => do not set the chrominance for last pixel - * - * YUV to RGB CONVERSION IS (OR SHOULD BE) ITU-R BT 601. - * - * THERE IS A LOT OF CODE REPETITION IN THIS ROUTINE IN ORDER TO AVOID - * INEFFICIENT SWITCHING INSIDE INNER LOOPS. REARRANGING THE LOGIC TO - * REDUCE CODE LENGTH WILL GENERALLY IMPAIR RUNTIME PERFORMANCE. BEWARE. - */ -/*---------------------------------------------------------------------------*/ -int -redaub(struct easycap *peasycap, void *pad, void *pex, int much, int more, - u8 mask, u8 margin, bool isuy) -{ - static s32 ay[256], bu[256], rv[256], gu[256], gv[256]; - u8 *pcache; - u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr; - int bytesperpixel; - bool byteswaporder, decimatepixel, last; - int j, rump; - s32 tmp; - - if (much % 2) { - SAM("MISTAKE: much is odd\n"); - return -EFAULT; - } - bytesperpixel = peasycap->bytesperpixel; - byteswaporder = peasycap->byteswaporder; - decimatepixel = peasycap->decimatepixel; - -/*---------------------------------------------------------------------------*/ - if (!bu[255]) { - for (j = 0; j < 112; j++) { - tmp = (0xFF00 & (453 * j)) >> 8; - bu[j + 128] = tmp; bu[127 - j] = -tmp; - tmp = (0xFF00 & (359 * j)) >> 8; - rv[j + 128] = tmp; rv[127 - j] = -tmp; - tmp = (0xFF00 & (88 * j)) >> 8; - gu[j + 128] = tmp; gu[127 - j] = -tmp; - tmp = (0xFF00 & (183 * j)) >> 8; - gv[j + 128] = tmp; gv[127 - j] = -tmp; - } - for (j = 0; j < 16; j++) { - bu[j] = bu[16]; rv[j] = rv[16]; - gu[j] = gu[16]; gv[j] = gv[16]; - } - for (j = 240; j < 256; j++) { - bu[j] = bu[239]; rv[j] = rv[239]; - gu[j] = gu[239]; gv[j] = gv[239]; - } - for (j = 16; j < 236; j++) - ay[j] = j; - for (j = 0; j < 16; j++) - ay[j] = ay[16]; - for (j = 236; j < 256; j++) - ay[j] = ay[235]; - JOM(8, "lookup tables are prepared\n"); - } - pcache = peasycap->pcache; - if (!pcache) - pcache = &peasycap->cache[0]; -/*---------------------------------------------------------------------------*/ -/* - * TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER - */ -/*---------------------------------------------------------------------------*/ - if (!pcache) { - SAM("MISTAKE: pcache is NULL\n"); - return -EFAULT; - } - - if (pcache != &peasycap->cache[0]) - JOM(16, "cache has %i bytes\n", (int)(pcache - &peasycap->cache[0])); - p2 = &peasycap->cache[0]; - p3 = (u8 *)pad - (int)(pcache - &peasycap->cache[0]); - while (p2 < pcache) { - *p3++ = *p2; p2++; - } - pcache = &peasycap->cache[0]; - if (p3 != pad) { - SAM("MISTAKE: pointer misalignment\n"); - return -EFAULT; - } -/*---------------------------------------------------------------------------*/ - rump = (int)(0x03 & mask); - u = 0; v = 0; - p2 = (u8 *)pex; pz = p2 + much; pr = p3 + more; last = false; - p2++; - - if (isuy) - u = *(p2 - 1); - else - v = *(p2 - 1); - - if (rump) - JOM(16, "%4i=much %4i=more %i=rump\n", much, more, rump); - -/*---------------------------------------------------------------------------*/ - switch (bytesperpixel) { - case 2: { - if (!decimatepixel) { - memcpy(pad, pex, (size_t)much); - if (!byteswaporder) { - /* UYVY */ - return 0; - } else { - /* YUYV */ - p3 = (u8 *)pad; pz = p3 + much; - while (pz > p3) { - c = *p3; - *p3 = *(p3 + 1); - *(p3 + 1) = c; - p3 += 2; - } - return 0; - } - } else { - if (!byteswaporder) { - /* UYVY DECIMATED */ - p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much; - while (pz > p2) { - *p3 = *p2; - *(p3 + 1) = *(p2 + 1); - *(p3 + 2) = *(p2 + 2); - *(p3 + 3) = *(p2 + 3); - p3 += 4; p2 += 8; - } - return 0; - } else { - /* YUYV DECIMATED */ - p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much; - while (pz > p2) { - *p3 = *(p2 + 1); - *(p3 + 1) = *p2; - *(p3 + 2) = *(p2 + 3); - *(p3 + 3) = *(p2 + 2); - p3 += 4; p2 += 8; - } - return 0; - } - } - break; - } - case 3: - { - if (!decimatepixel) { - if (!byteswaporder) { - /* RGB */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = r; - *pcache++ = g; - *pcache++ = b; - break; - } - case 2: { - *p3 = r; - *(p3 + 1) = g; - *pcache++ = b; - break; - } - default: { - SAM("MISTAKE: %i=rump\n", - bytesperpixel - rump); - return -EFAULT; - } - } - } else { - *p3 = r; - *(p3 + 1) = g; - *(p3 + 2) = b; - } - p2 += 2; - if (isuy) - isuy = false; - else - isuy = true; - p3 += bytesperpixel; - } - return 0; - } else { - /* BGR */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } - else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = b; - *pcache++ = g; - *pcache++ = r; - break; - } - case 2: { - *p3 = b; - *(p3 + 1) = g; - *pcache++ = r; - break; - } - default: { - SAM("MISTAKE: %i=rump\n", - bytesperpixel - rump); - return -EFAULT; - } - } - } else { - *p3 = b; - *(p3 + 1) = g; - *(p3 + 2) = r; - } - p2 += 2; - if (isuy) - isuy = false; - else - isuy = true; - p3 += bytesperpixel; - } - } - return 0; - } else { - if (!byteswaporder) { - /* RGB DECIMATED */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - if (isuy) { - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = r; - *pcache++ = g; - *pcache++ = b; - break; - } - case 2: { - *p3 = r; - *(p3 + 1) = g; - *pcache++ = b; - break; - } - default: { - SAM("MISTAKE: " - "%i=rump\n", - bytesperpixel - rump); - return -EFAULT; - } - } - } else { - *p3 = r; - *(p3 + 1) = g; - *(p3 + 2) = b; - } - isuy = false; - p3 += bytesperpixel; - } else { - isuy = true; - } - p2 += 2; - } - return 0; - } else { - /* BGR DECIMATED */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - if (isuy) { - - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = b; - *pcache++ = g; - *pcache++ = r; - break; - } - case 2: { - *p3 = b; - *(p3 + 1) = g; - *pcache++ = r; - break; - } - default: { - SAM("MISTAKE: " - "%i=rump\n", - bytesperpixel - rump); - return -EFAULT; - } - } - } else { - *p3 = b; - *(p3 + 1) = g; - *(p3 + 2) = r; - } - isuy = false; - p3 += bytesperpixel; - } - else - isuy = true; - p2 += 2; - } - return 0; - } - } - break; - } - case 4: - { - if (!decimatepixel) { - if (!byteswaporder) { - /* RGBA */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = r; - *pcache++ = g; - *pcache++ = b; - *pcache++ = 0; - break; - } - case 2: { - *p3 = r; - *(p3 + 1) = g; - *pcache++ = b; - *pcache++ = 0; - break; - } - case 3: { - *p3 = r; - *(p3 + 1) = g; - *(p3 + 2) = b; - *pcache++ = 0; - break; - } - default: { - SAM("MISTAKE: %i=rump\n", - bytesperpixel - rump); - return -EFAULT; - } - } - } else { - *p3 = r; - *(p3 + 1) = g; - *(p3 + 2) = b; - *(p3 + 3) = 0; - } - p2 += 2; - if (isuy) - isuy = false; - else - isuy = true; - p3 += bytesperpixel; - } - return 0; - } else { - /* - * BGRA - */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = b; - *pcache++ = g; - *pcache++ = r; - *pcache++ = 0; - break; - } - case 2: { - *p3 = b; - *(p3 + 1) = g; - *pcache++ = r; - *pcache++ = 0; - break; - } - case 3: { - *p3 = b; - *(p3 + 1) = g; - *(p3 + 2) = r; - *pcache++ = 0; - break; - } - default: - SAM("MISTAKE: %i=rump\n", - bytesperpixel - rump); - return -EFAULT; - } - } else { - *p3 = b; - *(p3 + 1) = g; - *(p3 + 2) = r; - *(p3 + 3) = 0; - } - p2 += 2; - if (isuy) - isuy = false; - else - isuy = true; - p3 += bytesperpixel; - } - } - return 0; - } else { - if (!byteswaporder) { - /* - * RGBA DECIMATED - */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - if (isuy) { - - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = r; - *pcache++ = g; - *pcache++ = b; - *pcache++ = 0; - break; - } - case 2: { - *p3 = r; - *(p3 + 1) = g; - *pcache++ = b; - *pcache++ = 0; - break; - } - case 3: { - *p3 = r; - *(p3 + 1) = g; - *(p3 + 2) = b; - *pcache++ = 0; - break; - } - default: { - SAM("MISTAKE: " - "%i=rump\n", - bytesperpixel - - rump); - return -EFAULT; - } - } - } else { - *p3 = r; - *(p3 + 1) = g; - *(p3 + 2) = b; - *(p3 + 3) = 0; - } - isuy = false; - p3 += bytesperpixel; - } else - isuy = true; - p2 += 2; - } - return 0; - } else { - /* - * BGRA DECIMATED - */ - while (pz > p2) { - if (pr <= (p3 + bytesperpixel)) - last = true; - else - last = false; - y = *p2; - if (last && (0x0C & mask)) { - if (0x04 & mask) { - if (isuy) - v = margin; - else - u = margin; - } else - if (0x08 & mask) - ; - } else { - if (isuy) - v = *(p2 + 1); - else - u = *(p2 + 1); - } - - if (isuy) { - tmp = ay[(int)y] + rv[(int)v]; - r = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] - gu[(int)u] - - gv[(int)v]; - g = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - tmp = ay[(int)y] + bu[(int)u]; - b = (255 < tmp) ? 255 : ((0 > tmp) ? - 0 : (u8)tmp); - - if (last && rump) { - pcache = &peasycap->cache[0]; - switch (bytesperpixel - rump) { - case 1: { - *p3 = b; - *pcache++ = g; - *pcache++ = r; - *pcache++ = 0; - break; - } - case 2: { - *p3 = b; - *(p3 + 1) = g; - *pcache++ = r; - *pcache++ = 0; - break; - } - case 3: { - *p3 = b; - *(p3 + 1) = g; - *(p3 + 2) = r; - *pcache++ = 0; - break; - } - default: { - SAM("MISTAKE: " - "%i=rump\n", - bytesperpixel - rump); - return -EFAULT; - } - } - } else { - *p3 = b; - *(p3 + 1) = g; - *(p3 + 2) = r; - *(p3 + 3) = 0; - } - isuy = false; - p3 += bytesperpixel; - } else - isuy = true; - p2 += 2; - } - return 0; - } - } - break; - } - default: { - SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel); - return -EFAULT; - } - } - return 0; -} -/*****************************************************************************/ -/* - * SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434 - */ -/*****************************************************************************/ -static void easycap_vma_open(struct vm_area_struct *pvma) -{ - struct easycap *peasycap; - - peasycap = pvma->vm_private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return; - } - peasycap->vma_many++; - JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many); - return; -} -/*****************************************************************************/ -static void easycap_vma_close(struct vm_area_struct *pvma) -{ - struct easycap *peasycap; - - peasycap = pvma->vm_private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return; - } - peasycap->vma_many--; - JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many); - return; -} -/*****************************************************************************/ -static int easycap_vma_fault(struct vm_area_struct *pvma, struct vm_fault *pvmf) -{ - int k, m, retcode; - void *pbuf; - struct page *page; - struct easycap *peasycap; - - retcode = VM_FAULT_NOPAGE; - - if (!pvma) { - SAY("pvma is NULL\n"); - return retcode; - } - if (!pvmf) { - SAY("pvmf is NULL\n"); - return retcode; - } - - k = (pvmf->pgoff) / (FRAME_BUFFER_SIZE/PAGE_SIZE); - m = (pvmf->pgoff) % (FRAME_BUFFER_SIZE/PAGE_SIZE); - - if (!m) - JOT(4, "%4i=k, %4i=m\n", k, m); - else - JOT(16, "%4i=k, %4i=m\n", k, m); - - if ((0 > k) || (FRAME_BUFFER_MANY <= k)) { - SAY("ERROR: buffer index %i out of range\n", k); - return retcode; - } - if ((0 > m) || (FRAME_BUFFER_SIZE/PAGE_SIZE <= m)) { - SAY("ERROR: page number %i out of range\n", m); - return retcode; - } - peasycap = pvma->vm_private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return retcode; - } -/*---------------------------------------------------------------------------*/ - pbuf = peasycap->frame_buffer[k][m].pgo; - if (!pbuf) { - SAM("ERROR: pbuf is NULL\n"); - return retcode; - } - page = virt_to_page(pbuf); - if (!page) { - SAM("ERROR: page is NULL\n"); - return retcode; - } - get_page(page); -/*---------------------------------------------------------------------------*/ - if (!page) { - SAM("ERROR: page is NULL after get_page(page)\n"); - } else { - pvmf->page = page; - retcode = VM_FAULT_MINOR; - } - return retcode; -} - -static const struct vm_operations_struct easycap_vm_ops = { - .open = easycap_vma_open, - .close = easycap_vma_close, - .fault = easycap_vma_fault, -}; - -static int easycap_mmap(struct file *file, struct vm_area_struct *pvma) -{ - JOT(8, "\n"); - - pvma->vm_ops = &easycap_vm_ops; - pvma->vm_flags |= VM_RESERVED; - if (file) - pvma->vm_private_data = file->private_data; - easycap_vma_open(pvma); - return 0; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS - * PROVIDED peasycap->video_idle IS ZERO. REGARDLESS OF THIS BEING TRUE, - * IT IS RESUBMITTED PROVIDED peasycap->video_isoc_streaming IS NOT ZERO. - * - * THIS FUNCTION IS AN INTERRUPT SERVICE ROUTINE AND MUST NOT SLEEP. - * - * INFORMATION ABOUT THE VALIDITY OF THE CONTENTS OF THE FIELD BUFFER ARE - * STORED IN THE TWO-BYTE STATUS PARAMETER - * peasycap->field_buffer[peasycap->field_fill][0].kount - * NOTICE THAT THE INFORMATION IS STORED ONLY WITH PAGE 0 OF THE FIELD BUFFER. - * - * THE LOWER BYTE CONTAINS THE FIELD PARITY BYTE FURNISHED BY THE SAA7113H - * CHIP. - * - * THE UPPER BYTE IS ZERO IF NO PROBLEMS, OTHERWISE: - * 0 != (kount & 0x8000) => AT LEAST ONE URB COMPLETED WITH ERRORS - * 0 != (kount & 0x4000) => BUFFER HAS TOO MUCH DATA - * 0 != (kount & 0x2000) => BUFFER HAS NOT ENOUGH DATA - * 0 != (kount & 0x1000) => BUFFER HAS DATA FROM DISPARATE INPUTS - * 0 != (kount & 0x0400) => RESERVED - * 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED - * 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY? - */ -/*---------------------------------------------------------------------------*/ -static void easycap_complete(struct urb *purb) -{ - struct easycap *peasycap; - struct data_buffer *pfield_buffer; - char errbuf[16]; - int i, more, much, leap, rc, last; - int videofieldamount; - unsigned int override, bad; - int framestatus, framelength, frameactual, frameoffset; - u8 *pu; - - if (!purb) { - SAY("ERROR: easycap_complete(): purb is NULL\n"); - return; - } - peasycap = purb->context; - if (!peasycap) { - SAY("ERROR: easycap_complete(): peasycap is NULL\n"); - return; - } - if (peasycap->video_eof) - return; - for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) - if (purb->transfer_buffer == peasycap->video_isoc_buffer[i].pgo) - break; - JOM(16, "%2i=urb\n", i); - last = peasycap->video_isoc_sequence; - if ((((VIDEO_ISOC_BUFFER_MANY - 1) == last) && (0 != i)) || - (((VIDEO_ISOC_BUFFER_MANY - 1) != last) && ((last + 1) != i))) { - JOM(16, "ERROR: out-of-order urbs %i,%i ... continuing\n", - last, i); - } - peasycap->video_isoc_sequence = i; - - if (peasycap->video_idle) { - JOM(16, "%i=video_idle %i=video_isoc_streaming\n", - peasycap->video_idle, peasycap->video_isoc_streaming); - if (peasycap->video_isoc_streaming) { - rc = usb_submit_urb(purb, GFP_ATOMIC); - if (rc) { - SAM("%s:%d ENOMEM\n", strerror(rc), rc); - if (-ENODEV != rc) - SAM("ERROR: while %i=video_idle, " - "usb_submit_urb() " - "failed with rc:\n", - peasycap->video_idle); - } - } - return; - } - override = 0; -/*---------------------------------------------------------------------------*/ - if (FIELD_BUFFER_MANY <= peasycap->field_fill) { - SAM("ERROR: bad peasycap->field_fill\n"); - return; - } - if (purb->status) { - if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) { - JOM(8, "urb status -ESHUTDOWN or -ENOENT\n"); - return; - } - - (peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ; - SAM("ERROR: bad urb status -%s: %d\n", - strerror(purb->status), purb->status); -/*---------------------------------------------------------------------------*/ - } else { - for (i = 0; i < purb->number_of_packets; i++) { - if (0 != purb->iso_frame_desc[i].status) { - (peasycap->field_buffer - [peasycap->field_fill][0].kount) |= 0x8000 ; - /* FIXME: 1. missing '-' check boundaries */ - strcpy(&errbuf[0], - strerror(purb->iso_frame_desc[i].status)); - } - framestatus = purb->iso_frame_desc[i].status; - framelength = purb->iso_frame_desc[i].length; - frameactual = purb->iso_frame_desc[i].actual_length; - frameoffset = purb->iso_frame_desc[i].offset; - - JOM(16, "frame[%2i]:" - "%4i=status " - "%4i=actual " - "%4i=length " - "%5i=offset\n", - i, framestatus, frameactual, framelength, frameoffset); - if (!purb->iso_frame_desc[i].status) { - more = purb->iso_frame_desc[i].actual_length; - pfield_buffer = &peasycap->field_buffer - [peasycap->field_fill][peasycap->field_page]; - videofieldamount = (peasycap->field_page * - PAGE_SIZE) + - (int)(pfield_buffer->pto - pfield_buffer->pgo); - if (4 == more) - peasycap->video_mt++; - if (4 < more) { - if (peasycap->video_mt) { - JOM(8, "%4i empty video urb frames\n", - peasycap->video_mt); - peasycap->video_mt = 0; - } - if (FIELD_BUFFER_MANY <= peasycap->field_fill) { - SAM("ERROR: bad peasycap->field_fill\n"); - return; - } - if (FIELD_BUFFER_SIZE/PAGE_SIZE <= - peasycap->field_page) { - SAM("ERROR: bad peasycap->field_page\n"); - return; - } - pfield_buffer = &peasycap->field_buffer - [peasycap->field_fill][peasycap->field_page]; - pu = (u8 *)(purb->transfer_buffer + - purb->iso_frame_desc[i].offset); - if (0x80 & *pu) - leap = 8; - else - leap = 4; -/*--------------------------------------------------------------------------*/ -/* - * EIGHT-BYTE END-OF-VIDEOFIELD MARKER. - * NOTE: A SUCCESSION OF URB FRAMES FOLLOWING THIS ARE EMPTY, - * CORRESPONDING TO THE FIELD FLYBACK (VERTICAL BLANKING) PERIOD. - * - * PROVIDED THE FIELD BUFFER CONTAINS GOOD DATA AS INDICATED BY A ZERO UPPER - * BYTE OF - * peasycap->field_buffer[peasycap->field_fill][0].kount - * THE CONTENTS OF THE FIELD BUFFER ARE OFFERED TO dqbuf(), field_read IS - * UPDATED AND field_fill IS BUMPED. IF THE FIELD BUFFER CONTAINS BAD DATA - * NOTHING IS OFFERED TO dqbuf(). - * - * THE DECISION ON WHETHER THE PARITY OF THE OFFERED FIELD BUFFER IS RIGHT - * RESTS WITH dqbuf(). - */ -/*---------------------------------------------------------------------------*/ - if ((8 == more) || override) { - if (videofieldamount > - peasycap->videofieldamount) { - if (2 == videofieldamount - - peasycap-> - videofieldamount) { - (peasycap->field_buffer - [peasycap->field_fill] - [0].kount) |= 0x0100; - peasycap->video_junk += (1 + - VIDEO_JUNK_TOLERATE); - } else - (peasycap->field_buffer - [peasycap->field_fill] - [0].kount) |= 0x4000; - } else if (videofieldamount < - peasycap-> - videofieldamount) { - (peasycap->field_buffer - [peasycap->field_fill] - [0].kount) |= 0x2000; - } - bad = 0xFF00 & peasycap->field_buffer - [peasycap->field_fill] - [0].kount; - if (!bad) { - (peasycap->video_junk)--; - if (-VIDEO_JUNK_TOLERATE > - peasycap->video_junk) - peasycap->video_junk = - -VIDEO_JUNK_TOLERATE; - peasycap->field_read = - (peasycap-> - field_fill)++; - if (FIELD_BUFFER_MANY <= - peasycap-> - field_fill) - peasycap-> - field_fill = 0; - peasycap->field_page = 0; - pfield_buffer = &peasycap-> - field_buffer - [peasycap-> - field_fill] - [peasycap-> - field_page]; - pfield_buffer->pto = - pfield_buffer->pgo; - JOM(8, "bumped to: %i=" - "peasycap->" - "field_fill %i=" - "parity\n", - peasycap->field_fill, - 0x00FF & - pfield_buffer->kount); - JOM(8, "field buffer %i has " - "%i bytes fit to be " - "read\n", - peasycap->field_read, - videofieldamount); - JOM(8, "wakeup call to " - "wq_video, " - "%i=field_read " - "%i=field_fill " - "%i=parity\n", - peasycap->field_read, - peasycap->field_fill, - 0x00FF & peasycap-> - field_buffer - [peasycap-> - field_read][0].kount); - wake_up_interruptible - (&(peasycap-> - wq_video)); - } else { - peasycap->video_junk++; - if (bad & 0x0010) - peasycap->video_junk += - (1 + VIDEO_JUNK_TOLERATE/2); - JOM(8, "field buffer %i had %i " - "bytes, now discarded: " - "0x%04X\n", - peasycap->field_fill, - videofieldamount, - (0xFF00 & - peasycap->field_buffer - [peasycap->field_fill][0]. - kount)); - (peasycap->field_fill)++; - - if (FIELD_BUFFER_MANY <= - peasycap->field_fill) - peasycap->field_fill = 0; - peasycap->field_page = 0; - pfield_buffer = - &peasycap->field_buffer - [peasycap->field_fill] - [peasycap->field_page]; - pfield_buffer->pto = - pfield_buffer->pgo; - - JOM(8, "bumped to: %i=peasycap->" - "field_fill %i=parity\n", - peasycap->field_fill, - 0x00FF & pfield_buffer->kount); - } - if (8 == more) { - JOM(8, "end-of-field: received " - "parity byte 0x%02X\n", - (0xFF & *pu)); - if (0x40 & *pu) - pfield_buffer->kount = 0x0000; - else - pfield_buffer->kount = 0x0001; - pfield_buffer->input = 0x08 | - (0x07 & peasycap->input); - JOM(8, "end-of-field: 0x%02X=kount\n", - 0xFF & pfield_buffer->kount); - } - } -/*---------------------------------------------------------------------------*/ -/* - * COPY more BYTES FROM ISOC BUFFER TO FIELD BUFFER - */ -/*---------------------------------------------------------------------------*/ - pu += leap; - more -= leap; - - if (FIELD_BUFFER_MANY <= peasycap->field_fill) { - SAM("ERROR: bad peasycap->field_fill\n"); - return; - } - if (FIELD_BUFFER_SIZE/PAGE_SIZE <= peasycap->field_page) { - SAM("ERROR: bad peasycap->field_page\n"); - return; - } - pfield_buffer = &peasycap->field_buffer - [peasycap->field_fill][peasycap->field_page]; - while (more) { - pfield_buffer = &peasycap->field_buffer - [peasycap->field_fill] - [peasycap->field_page]; - if (PAGE_SIZE < (pfield_buffer->pto - - pfield_buffer->pgo)) { - SAM("ERROR: bad pfield_buffer->pto\n"); - return; - } - if (PAGE_SIZE == (pfield_buffer->pto - - pfield_buffer->pgo)) { - (peasycap->field_page)++; - if (FIELD_BUFFER_SIZE/PAGE_SIZE <= - peasycap->field_page) { - JOM(16, "wrapping peasycap->" - "field_page\n"); - peasycap->field_page = 0; - } - pfield_buffer = &peasycap-> - field_buffer - [peasycap->field_fill] - [peasycap->field_page]; - pfield_buffer->pto = pfield_buffer->pgo; - pfield_buffer->input = 0x08 | - (0x07 & peasycap->input); - if ((peasycap->field_buffer[peasycap-> - field_fill][0]). - input != - pfield_buffer->input) - (peasycap->field_buffer - [peasycap->field_fill] - [0]).kount |= 0x1000; - } - - much = PAGE_SIZE - - (int)(pfield_buffer->pto - - pfield_buffer->pgo); - - if (much > more) - much = more; - memcpy(pfield_buffer->pto, pu, much); - pu += much; - (pfield_buffer->pto) += much; - more -= much; - } - } - } - } - } -/*---------------------------------------------------------------------------*/ -/* - * RESUBMIT THIS URB, UNLESS A SEVERE PERSISTENT ERROR CONDITION EXISTS. - * - * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO AN ERROR CONDITION - * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE. - */ -/*---------------------------------------------------------------------------*/ - if (VIDEO_ISOC_BUFFER_MANY <= peasycap->video_junk) { - SAM("easycap driver shutting down on condition green\n"); - peasycap->status = 1; - peasycap->video_eof = 1; - peasycap->video_junk = 0; - wake_up_interruptible(&peasycap->wq_video); -#if !defined(PERSEVERE) - peasycap->audio_eof = 1; - wake_up_interruptible(&peasycap->wq_audio); -#endif /*PERSEVERE*/ - return; - } - if (peasycap->video_isoc_streaming) { - rc = usb_submit_urb(purb, GFP_ATOMIC); - if (rc) { - SAM("%s: %d\n", strerror(rc), rc); - if (-ENODEV != rc) - SAM("ERROR: while %i=video_idle, " - "usb_submit_urb() " - "failed with rc:\n", - peasycap->video_idle); - } - } - return; -} -static const struct file_operations easycap_fops = { - .owner = THIS_MODULE, - .open = easycap_open, - .unlocked_ioctl = easycap_unlocked_ioctl, - .poll = easycap_poll, - .mmap = easycap_mmap, - .llseek = no_llseek, -}; -static const struct usb_class_driver easycap_class = { - .name = "usb/easycap%d", - .fops = &easycap_fops, - .minor_base = USB_SKEL_MINOR_BASE, -}; -/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ -static const struct v4l2_file_operations v4l2_fops = { - .owner = THIS_MODULE, - .open = easycap_open_noinode, - .unlocked_ioctl = easycap_unlocked_ioctl, - .poll = easycap_poll, - .mmap = easycap_mmap, -}; -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE - * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE. - */ -/*---------------------------------------------------------------------------*/ -static int easycap_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *usbdev; - struct usb_host_interface *alt; - struct usb_endpoint_descriptor *ep; - struct usb_interface_descriptor *interface; - struct urb *purb; - struct easycap *peasycap; - int ndong; - struct data_urb *pdata_urb; - int i, j, k, m, rc; - u8 bInterfaceNumber; - u8 bInterfaceClass; - u8 bInterfaceSubClass; - void *pbuf; - int okalt[8], isokalt; - int okepn[8]; - int okmps[8]; - int maxpacketsize; - u16 mask; - s32 value; - struct easycap_format *peasycap_format; - int fmtidx; - struct inputset *inputset; - - usbdev = interface_to_usbdev(intf); - -/*---------------------------------------------------------------------------*/ - alt = usb_altnum_to_altsetting(intf, 0); - if (!alt) { - SAY("ERROR: usb_host_interface not found\n"); - return -EFAULT; - } - interface = &alt->desc; - if (!interface) { - SAY("ERROR: intf_descriptor is NULL\n"); - return -EFAULT; - } -/*---------------------------------------------------------------------------*/ -/* - * GET PROPERTIES OF PROBED INTERFACE - */ -/*---------------------------------------------------------------------------*/ - bInterfaceNumber = interface->bInterfaceNumber; - bInterfaceClass = interface->bInterfaceClass; - bInterfaceSubClass = interface->bInterfaceSubClass; - - JOT(4, "intf[%i]: num_altsetting=%i\n", - bInterfaceNumber, intf->num_altsetting); - JOT(4, "intf[%i]: cur_altsetting - altsetting=%li\n", - bInterfaceNumber, - (long int)(intf->cur_altsetting - intf->altsetting)); - JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n", - bInterfaceNumber, bInterfaceClass, bInterfaceSubClass); -/*---------------------------------------------------------------------------*/ -/* - * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED. - * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS - * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS - * PHYSICALLY UNPLUGGED. - * - * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN - * INTERFACES 1 AND 2 ARE PROBED. -*/ -/*---------------------------------------------------------------------------*/ - if (0 == bInterfaceNumber) { - peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); - if (!peasycap) { - SAY("ERROR: Could not allocate peasycap\n"); - return -ENOMEM; - } -/*---------------------------------------------------------------------------*/ -/* - * PERFORM URGENT INTIALIZATIONS ... -*/ -/*---------------------------------------------------------------------------*/ - peasycap->minor = -1; - kref_init(&peasycap->kref); - JOM(8, "intf[%i]: after kref_init(..._video) " - "%i=peasycap->kref.refcount.counter\n", - bInterfaceNumber, peasycap->kref.refcount.counter); - - /* module params */ - peasycap->gain = (s8)clamp(easycap_gain, 0, 31); - - init_waitqueue_head(&peasycap->wq_video); - init_waitqueue_head(&peasycap->wq_audio); - init_waitqueue_head(&peasycap->wq_trigger); - - if (mutex_lock_interruptible(&mutex_dongle)) { - SAY("ERROR: cannot down mutex_dongle\n"); - return -ERESTARTSYS; - } else { -/*---------------------------------------------------------------------------*/ - /* - * FOR INTERFACES 1 AND 2 THE POINTER peasycap WILL NEED TO - * TO BE THE SAME AS THAT ALLOCATED NOW FOR INTERFACE 0. - * - * NORMALLY ndong WILL NOT HAVE CHANGED SINCE INTERFACE 0 WAS - * PROBED, BUT THIS MAY NOT BE THE CASE IF, FOR EXAMPLE, TWO - * EASYCAPs ARE PLUGGED IN SIMULTANEOUSLY. - */ -/*---------------------------------------------------------------------------*/ - for (ndong = 0; ndong < DONGLE_MANY; ndong++) { - if ((!easycapdc60_dongle[ndong].peasycap) && - (!mutex_is_locked(&easycapdc60_dongle - [ndong].mutex_video)) && - (!mutex_is_locked(&easycapdc60_dongle - [ndong].mutex_audio))) { - easycapdc60_dongle[ndong].peasycap = peasycap; - peasycap->isdongle = ndong; - JOM(8, "intf[%i]: peasycap-->easycap" - "_dongle[%i].peasycap\n", - bInterfaceNumber, ndong); - break; - } - } - if (DONGLE_MANY <= ndong) { - SAM("ERROR: too many dongles\n"); - mutex_unlock(&mutex_dongle); - return -ENOMEM; - } - mutex_unlock(&mutex_dongle); - } - peasycap->allocation_video_struct = sizeof(struct easycap); - peasycap->allocation_video_page = 0; - peasycap->allocation_video_urb = 0; - peasycap->allocation_audio_struct = 0; - peasycap->allocation_audio_page = 0; - peasycap->allocation_audio_urb = 0; - -/*---------------------------------------------------------------------------*/ -/* - * ... AND FURTHER INITIALIZE THE STRUCTURE -*/ -/*---------------------------------------------------------------------------*/ - peasycap->pusb_device = usbdev; - peasycap->pusb_interface = intf; - - peasycap->ilk = 0; - peasycap->microphone = false; - - peasycap->video_interface = -1; - peasycap->video_altsetting_on = -1; - peasycap->video_altsetting_off = -1; - peasycap->video_endpointnumber = -1; - peasycap->video_isoc_maxframesize = -1; - peasycap->video_isoc_buffer_size = -1; - - peasycap->audio_interface = -1; - peasycap->audio_altsetting_on = -1; - peasycap->audio_altsetting_off = -1; - peasycap->audio_endpointnumber = -1; - peasycap->audio_isoc_maxframesize = -1; - peasycap->audio_isoc_buffer_size = -1; - - peasycap->frame_buffer_many = FRAME_BUFFER_MANY; - - for (k = 0; k < INPUT_MANY; k++) - peasycap->lost[k] = 0; - peasycap->skip = 0; - peasycap->skipped = 0; - peasycap->offerfields = 0; -/*---------------------------------------------------------------------------*/ -/* - * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ... - */ -/*---------------------------------------------------------------------------*/ - rc = fillin_formats(); - if (0 > rc) { - SAM("ERROR: fillin_formats() rc = %i\n", rc); - return -EFAULT; - } - JOM(4, "%i formats available\n", rc); -/*---------------------------------------------------------------------------*/ -/* - * ... AND POPULATE easycap.inputset[] -*/ -/*---------------------------------------------------------------------------*/ - /* FIXME: maybe we just use memset 0 */ - inputset = peasycap->inputset; - for (k = 0; k < INPUT_MANY; k++) { - inputset[k].input_ok = 0; - inputset[k].standard_offset_ok = 0; - inputset[k].format_offset_ok = 0; - inputset[k].brightness_ok = 0; - inputset[k].contrast_ok = 0; - inputset[k].saturation_ok = 0; - inputset[k].hue_ok = 0; - } - - fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; - m = 0; - mask = 0; - for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) { - if (fmtidx == easycap_standard[i].v4l2_standard.index) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].standard_offset = i; - - mask = easycap_standard[i].mask; - } - } - - if (1 != m) { - SAM("ERROR: " - "inputset->standard_offset unpopulated, %i=m\n", m); - return -ENOENT; - } - - peasycap_format = &easycap_format[0]; - m = 0; - for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) { - struct v4l2_pix_format *pix = - &peasycap_format->v4l2_format.fmt.pix; - if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) && - pix->field == V4L2_FIELD_NONE && - pix->pixelformat == V4L2_PIX_FMT_UYVY && - pix->width == 640 && pix->height == 480) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].format_offset = i; - break; - } - peasycap_format++; - } - if (1 != m) { - SAM("ERROR: inputset[]->format_offset unpopulated\n"); - return -ENOENT; - } - - m = 0; - for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) { - value = easycap_control[i].default_value; - if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].brightness = value; - } else if (V4L2_CID_CONTRAST == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].contrast = value; - } else if (V4L2_CID_SATURATION == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].saturation = value; - } else if (V4L2_CID_HUE == easycap_control[i].id) { - m++; - for (k = 0; k < INPUT_MANY; k++) - inputset[k].hue = value; - } - } - - if (4 != m) { - SAM("ERROR: inputset[]->brightness underpopulated\n"); - return -ENOENT; - } - for (k = 0; k < INPUT_MANY; k++) - inputset[k].input = k; - JOM(4, "populated inputset[]\n"); - JOM(4, "finished initialization\n"); - } else { -/*---------------------------------------------------------------------------*/ -/* - * FIXME - * - * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2. - * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE. - */ -/*---------------------------------------------------------------------------*/ - for (ndong = 0; ndong < DONGLE_MANY; ndong++) { - if (usbdev == easycapdc60_dongle[ndong].peasycap-> - pusb_device) { - peasycap = easycapdc60_dongle[ndong].peasycap; - JOT(8, "intf[%i]: dongle[%i].peasycap\n", - bInterfaceNumber, ndong); - break; - } - } - if (DONGLE_MANY <= ndong) { - SAY("ERROR: peasycap is unknown when probing interface %i\n", - bInterfaceNumber); - return -ENODEV; - } - if (!peasycap) { - SAY("ERROR: peasycap is NULL when probing interface %i\n", - bInterfaceNumber); - return -ENODEV; - } - } -/*---------------------------------------------------------------------------*/ - if ((USB_CLASS_VIDEO == bInterfaceClass) || - (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { - if (-1 == peasycap->video_interface) { - peasycap->video_interface = bInterfaceNumber; - JOM(4, "setting peasycap->video_interface=%i\n", - peasycap->video_interface); - } else { - if (peasycap->video_interface != bInterfaceNumber) { - SAM("ERROR: attempting to reset " - "peasycap->video_interface\n"); - SAM("...... continuing with " - "%i=peasycap->video_interface\n", - peasycap->video_interface); - } - } - } else if ((USB_CLASS_AUDIO == bInterfaceClass) && - (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) { - if (-1 == peasycap->audio_interface) { - peasycap->audio_interface = bInterfaceNumber; - JOM(4, "setting peasycap->audio_interface=%i\n", - peasycap->audio_interface); - } else { - if (peasycap->audio_interface != bInterfaceNumber) { - SAM("ERROR: attempting to reset " - "peasycap->audio_interface\n"); - SAM("...... continuing with " - "%i=peasycap->audio_interface\n", - peasycap->audio_interface); - } - } - } -/*---------------------------------------------------------------------------*/ -/* - * INVESTIGATE ALL ALTSETTINGS. - * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS. - */ -/*---------------------------------------------------------------------------*/ - isokalt = 0; - - for (i = 0; i < intf->num_altsetting; i++) { - alt = usb_altnum_to_altsetting(intf, i); - if (!alt) { - SAM("ERROR: alt is NULL\n"); - return -EFAULT; - } - interface = &alt->desc; - if (!interface) { - SAM("ERROR: intf_descriptor is NULL\n"); - return -EFAULT; - } - - if (0 == interface->bNumEndpoints) - JOM(4, "intf[%i]alt[%i] has no endpoints\n", - bInterfaceNumber, i); -/*---------------------------------------------------------------------------*/ - for (j = 0; j < interface->bNumEndpoints; j++) { - ep = &alt->endpoint[j].desc; - if (!ep) { - SAM("ERROR: ep is NULL.\n"); - SAM("...... skipping\n"); - continue; - } - - if (!usb_endpoint_is_isoc_in(ep)) { - JOM(4, "intf[%i]alt[%i]end[%i] is a %d endpoint\n", - bInterfaceNumber, - i, j, ep->bmAttributes); - if (usb_endpoint_dir_out(ep)) { - SAM("ERROR: OUT endpoint unexpected\n"); - SAM("...... continuing\n"); - } - continue; - } - switch (bInterfaceClass) { - case USB_CLASS_VIDEO: - case USB_CLASS_VENDOR_SPEC: { - if (ep->wMaxPacketSize) { - if (8 > isokalt) { - okalt[isokalt] = i; - JOM(4, - "%i=okalt[%i]\n", - okalt[isokalt], - isokalt); - okepn[isokalt] = - ep-> - bEndpointAddress & - 0x0F; - JOM(4, - "%i=okepn[%i]\n", - okepn[isokalt], - isokalt); - okmps[isokalt] = - le16_to_cpu(ep-> - wMaxPacketSize); - JOM(4, - "%i=okmps[%i]\n", - okmps[isokalt], - isokalt); - isokalt++; - } - } else { - if (-1 == peasycap-> - video_altsetting_off) { - peasycap-> - video_altsetting_off = - i; - JOM(4, "%i=video_" - "altsetting_off " - "<====\n", - peasycap-> - video_altsetting_off); - } else { - SAM("ERROR: peasycap" - "->video_altsetting_" - "off already set\n"); - SAM("...... " - "continuing with " - "%i=peasycap->video_" - "altsetting_off\n", - peasycap-> - video_altsetting_off); - } - } - break; - } - case USB_CLASS_AUDIO: { - if (bInterfaceSubClass != - USB_SUBCLASS_AUDIOSTREAMING) - break; - if (!peasycap) { - SAM("MISTAKE: " - "peasycap is NULL\n"); - return -EFAULT; - } - if (ep->wMaxPacketSize) { - if (8 > isokalt) { - okalt[isokalt] = i ; - JOM(4, - "%i=okalt[%i]\n", - okalt[isokalt], - isokalt); - okepn[isokalt] = - ep-> - bEndpointAddress & - 0x0F; - JOM(4, - "%i=okepn[%i]\n", - okepn[isokalt], - isokalt); - okmps[isokalt] = - le16_to_cpu(ep-> - wMaxPacketSize); - JOM(4, - "%i=okmps[%i]\n", - okmps[isokalt], - isokalt); - isokalt++; - } - } else { - if (-1 == peasycap-> - audio_altsetting_off) { - peasycap-> - audio_altsetting_off = - i; - JOM(4, "%i=audio_" - "altsetting_off " - "<====\n", - peasycap-> - audio_altsetting_off); - } else { - SAM("ERROR: peasycap" - "->audio_altsetting_" - "off already set\n"); - SAM("...... " - "continuing with " - "%i=peasycap->" - "audio_altsetting_" - "off\n", - peasycap-> - audio_altsetting_off); - } - } - break; - } - default: - break; - } - if (0 == ep->wMaxPacketSize) { - JOM(4, "intf[%i]alt[%i]end[%i] " - "has zero packet size\n", - bInterfaceNumber, i, j); - } - } - } -/*---------------------------------------------------------------------------*/ -/* - * PERFORM INITIALIZATION OF THE PROBED INTERFACE - */ -/*---------------------------------------------------------------------------*/ - JOM(4, "initialization begins for interface %i\n", - interface->bInterfaceNumber); - switch (bInterfaceNumber) { -/*---------------------------------------------------------------------------*/ -/* - * INTERFACE 0 IS THE VIDEO INTERFACE - */ -/*---------------------------------------------------------------------------*/ - case 0: { - if (!peasycap) { - SAM("MISTAKE: peasycap is NULL\n"); - return -EFAULT; - } - if (!isokalt) { - SAM("ERROR: no viable video_altsetting_on\n"); - return -ENOENT; - } else { - peasycap->video_altsetting_on = okalt[isokalt - 1]; - JOM(4, "%i=video_altsetting_on <====\n", - peasycap->video_altsetting_on); - } -/*---------------------------------------------------------------------------*/ -/* - * DECIDE THE VIDEO STREAMING PARAMETERS - */ -/*---------------------------------------------------------------------------*/ - peasycap->video_endpointnumber = okepn[isokalt - 1]; - JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber); - maxpacketsize = okmps[isokalt - 1]; - - peasycap->video_isoc_maxframesize = - min(maxpacketsize, USB_2_0_MAXPACKETSIZE); - if (0 >= peasycap->video_isoc_maxframesize) { - SAM("ERROR: bad video_isoc_maxframesize\n"); - SAM(" possibly because port is USB 1.1\n"); - return -ENOENT; - } - JOM(4, "%i=video_isoc_maxframesize\n", - peasycap->video_isoc_maxframesize); - - peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC; - JOM(4, "%i=video_isoc_framesperdesc\n", - peasycap->video_isoc_framesperdesc); - if (0 >= peasycap->video_isoc_framesperdesc) { - SAM("ERROR: bad video_isoc_framesperdesc\n"); - return -ENOENT; - } - peasycap->video_isoc_buffer_size = - peasycap->video_isoc_maxframesize * - peasycap->video_isoc_framesperdesc; - JOM(4, "%i=video_isoc_buffer_size\n", - peasycap->video_isoc_buffer_size); - if ((PAGE_SIZE << VIDEO_ISOC_ORDER) < - peasycap->video_isoc_buffer_size) { - SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n"); - return -EFAULT; - } -/*---------------------------------------------------------------------------*/ - if (-1 == peasycap->video_interface) { - SAM("MISTAKE: video_interface is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->video_altsetting_on) { - SAM("MISTAKE: video_altsetting_on is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->video_altsetting_off) { - SAM("MISTAKE: video_interface_off is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->video_endpointnumber) { - SAM("MISTAKE: video_endpointnumber is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->video_isoc_maxframesize) { - SAM("MISTAKE: video_isoc_maxframesize is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->video_isoc_buffer_size) { - SAM("MISTAKE: video_isoc_buffer_size is unset\n"); - return -EFAULT; - } -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST. - */ -/*---------------------------------------------------------------------------*/ - INIT_LIST_HEAD(&(peasycap->urb_video_head)); - peasycap->purb_video_head = &(peasycap->urb_video_head); -/*---------------------------------------------------------------------------*/ - JOM(4, "allocating %i frame buffers of size %li\n", - FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); - JOM(4, ".... each scattered over %li pages\n", - FRAME_BUFFER_SIZE/PAGE_SIZE); - - for (k = 0; k < FRAME_BUFFER_MANY; k++) { - for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->frame_buffer[k][m].pgo) - SAM("attempting to reallocate frame " - " buffers\n"); - else { - pbuf = (void *)__get_free_page(GFP_KERNEL); - if (!pbuf) { - SAM("ERROR: Could not allocate frame " - "buffer %i page %i\n", k, m); - return -ENOMEM; - } else - peasycap->allocation_video_page += 1; - peasycap->frame_buffer[k][m].pgo = pbuf; - } - peasycap->frame_buffer[k][m].pto = - peasycap->frame_buffer[k][m].pgo; - } - } - - peasycap->frame_fill = 0; - peasycap->frame_read = 0; - JOM(4, "allocation of frame buffers done: %i pages\n", k * - m); -/*---------------------------------------------------------------------------*/ - JOM(4, "allocating %i field buffers of size %li\n", - FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); - JOM(4, ".... each scattered over %li pages\n", - FIELD_BUFFER_SIZE/PAGE_SIZE); - - for (k = 0; k < FIELD_BUFFER_MANY; k++) { - for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { - if (peasycap->field_buffer[k][m].pgo) { - SAM("ERROR: attempting to reallocate " - "field buffers\n"); - } else { - pbuf = (void *) __get_free_page(GFP_KERNEL); - if (!pbuf) { - SAM("ERROR: Could not allocate field" - " buffer %i page %i\n", k, m); - return -ENOMEM; - } - else - peasycap->allocation_video_page += 1; - peasycap->field_buffer[k][m].pgo = pbuf; - } - peasycap->field_buffer[k][m].pto = - peasycap->field_buffer[k][m].pgo; - } - peasycap->field_buffer[k][0].kount = 0x0200; - } - peasycap->field_fill = 0; - peasycap->field_page = 0; - peasycap->field_read = 0; - JOM(4, "allocation of field buffers done: %i pages\n", k * - m); -/*---------------------------------------------------------------------------*/ - JOM(4, "allocating %i isoc video buffers of size %i\n", - VIDEO_ISOC_BUFFER_MANY, - peasycap->video_isoc_buffer_size); - JOM(4, ".... each occupying contiguous memory pages\n"); - - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - pbuf = (void *)__get_free_pages(GFP_KERNEL, - VIDEO_ISOC_ORDER); - if (!pbuf) { - SAM("ERROR: Could not allocate isoc video buffer " - "%i\n", k); - return -ENOMEM; - } else - peasycap->allocation_video_page += - BIT(VIDEO_ISOC_ORDER); - - peasycap->video_isoc_buffer[k].pgo = pbuf; - peasycap->video_isoc_buffer[k].pto = - pbuf + peasycap->video_isoc_buffer_size; - peasycap->video_isoc_buffer[k].kount = k; - } - JOM(4, "allocation of isoc video buffers done: %i pages\n", - k * (0x01 << VIDEO_ISOC_ORDER)); -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... - */ -/*---------------------------------------------------------------------------*/ - JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); - JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", - peasycap->video_isoc_framesperdesc); - JOM(4, "using %i=peasycap->video_isoc_maxframesize\n", - peasycap->video_isoc_maxframesize); - JOM(4, "using %i=peasycap->video_isoc_buffer_sizen", - peasycap->video_isoc_buffer_size); - - for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { - purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc, - GFP_KERNEL); - if (!purb) { - SAM("ERROR: usb_alloc_urb returned NULL for buffer " - "%i\n", k); - return -ENOMEM; - } else - peasycap->allocation_video_urb += 1; -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); - if (!pdata_urb) { - SAM("ERROR: Could not allocate struct data_urb.\n"); - return -ENOMEM; - } else - peasycap->allocation_video_struct += - sizeof(struct data_urb); - - pdata_urb->purb = purb; - pdata_urb->isbuf = k; - pdata_urb->length = 0; - list_add_tail(&(pdata_urb->list_head), - peasycap->purb_video_head); -/*---------------------------------------------------------------------------*/ -/* - * ... AND INITIALIZE THEM - */ -/*---------------------------------------------------------------------------*/ - if (!k) { - JOM(4, "initializing video urbs thus:\n"); - JOM(4, " purb->interval = 1;\n"); - JOM(4, " purb->dev = peasycap->pusb_device;\n"); - JOM(4, " purb->pipe = usb_rcvisocpipe" - "(peasycap->pusb_device,%i);\n", - peasycap->video_endpointnumber); - JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); - JOM(4, " purb->transfer_buffer = peasycap->" - "video_isoc_buffer[.].pgo;\n"); - JOM(4, " purb->transfer_buffer_length = %i;\n", - peasycap->video_isoc_buffer_size); - JOM(4, " purb->complete = easycap_complete;\n"); - JOM(4, " purb->context = peasycap;\n"); - JOM(4, " purb->start_frame = 0;\n"); - JOM(4, " purb->number_of_packets = %i;\n", - peasycap->video_isoc_framesperdesc); - JOM(4, " for (j = 0; j < %i; j++)\n", - peasycap->video_isoc_framesperdesc); - JOM(4, " {\n"); - JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", - peasycap->video_isoc_maxframesize); - JOM(4, " purb->iso_frame_desc[j].length = %i;\n", - peasycap->video_isoc_maxframesize); - JOM(4, " }\n"); - } - - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, - peasycap->video_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo; - purb->transfer_buffer_length = - peasycap->video_isoc_buffer_size; - purb->complete = easycap_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = peasycap->video_isoc_framesperdesc; - for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j].offset = j * - peasycap->video_isoc_maxframesize; - purb->iso_frame_desc[j].length = - peasycap->video_isoc_maxframesize; - } - } - JOM(4, "allocation of %i struct urb done.\n", k); -/*--------------------------------------------------------------------------*/ -/* - * SAVE POINTER peasycap IN THIS INTERFACE. - */ -/*--------------------------------------------------------------------------*/ - usb_set_intfdata(intf, peasycap); -/*---------------------------------------------------------------------------*/ -/* - * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER, - * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE - * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH. - * BEWARE. -*/ -/*---------------------------------------------------------------------------*/ - peasycap->ntsc = easycap_ntsc; - JOM(8, "defaulting initially to %s\n", - easycap_ntsc ? "NTSC" : "PAL"); - rc = reset(peasycap); - if (rc) { - SAM("ERROR: reset() rc = %i\n", rc); - return -EFAULT; - } -/*--------------------------------------------------------------------------*/ -/* - * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. - */ -/*--------------------------------------------------------------------------*/ - if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) { - SAM("v4l2_device_register() failed\n"); - return -ENODEV; - } - JOM(4, "registered device instance: %s\n", - peasycap->v4l2_device.name); -/*---------------------------------------------------------------------------*/ -/* - * FIXME - * - * - * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG: -*/ -/*---------------------------------------------------------------------------*/ - peasycap->video_device.v4l2_dev = NULL; -/*---------------------------------------------------------------------------*/ - - - strcpy(&peasycap->video_device.name[0], "easycapdc60"); - peasycap->video_device.fops = &v4l2_fops; - peasycap->video_device.minor = -1; - peasycap->video_device.release = (void *)(&videodev_release); - - video_set_drvdata(&(peasycap->video_device), (void *)peasycap); - - if (0 != (video_register_device(&(peasycap->video_device), - VFL_TYPE_GRABBER, -1))) { - err("Not able to register with videodev"); - videodev_release(&(peasycap->video_device)); - return -ENODEV; - } else { - (peasycap->registered_video)++; - SAM("registered with videodev: %i=minor\n", - peasycap->video_device.minor); - peasycap->minor = peasycap->video_device.minor; - } -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - break; - } -/*--------------------------------------------------------------------------*/ -/* - * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE - * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE - */ -/*--------------------------------------------------------------------------*/ - case 1: { - if (!peasycap) { - SAM("MISTAKE: peasycap is NULL\n"); - return -EFAULT; - } -/*--------------------------------------------------------------------------*/ -/* - * SAVE POINTER peasycap IN INTERFACE 1 - */ -/*--------------------------------------------------------------------------*/ - usb_set_intfdata(intf, peasycap); - JOM(4, "no initialization required for interface %i\n", - interface->bInterfaceNumber); - break; - } -/*--------------------------------------------------------------------------*/ - case 2: { - if (!peasycap) { - SAM("MISTAKE: peasycap is NULL\n"); - return -EFAULT; - } - if (!isokalt) { - SAM("ERROR: no viable audio_altsetting_on\n"); - return -ENOENT; - } else { - peasycap->audio_altsetting_on = okalt[isokalt - 1]; - JOM(4, "%i=audio_altsetting_on <====\n", - peasycap->audio_altsetting_on); - } - - peasycap->audio_endpointnumber = okepn[isokalt - 1]; - JOM(4, "%i=audio_endpointnumber\n", peasycap->audio_endpointnumber); - - peasycap->audio_isoc_maxframesize = okmps[isokalt - 1]; - JOM(4, "%i=audio_isoc_maxframesize\n", - peasycap->audio_isoc_maxframesize); - if (0 >= peasycap->audio_isoc_maxframesize) { - SAM("ERROR: bad audio_isoc_maxframesize\n"); - return -ENOENT; - } - if (9 == peasycap->audio_isoc_maxframesize) { - peasycap->ilk |= 0x02; - SAM("audio hardware is microphone\n"); - peasycap->microphone = true; - peasycap->audio_pages_per_fragment = - PAGES_PER_AUDIO_FRAGMENT; - } else if (256 == peasycap->audio_isoc_maxframesize) { - peasycap->ilk &= ~0x02; - SAM("audio hardware is AC'97\n"); - peasycap->microphone = false; - peasycap->audio_pages_per_fragment = - PAGES_PER_AUDIO_FRAGMENT; - } else { - SAM("hardware is unidentified:\n"); - SAM("%i=audio_isoc_maxframesize\n", - peasycap->audio_isoc_maxframesize); - return -ENOENT; - } - - peasycap->audio_bytes_per_fragment = - peasycap->audio_pages_per_fragment * PAGE_SIZE; - peasycap->audio_buffer_page_many = (AUDIO_FRAGMENT_MANY * - peasycap->audio_pages_per_fragment); - - JOM(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY); - JOM(4, "%6i=audio_pages_per_fragment\n", - peasycap->audio_pages_per_fragment); - JOM(4, "%6i=audio_bytes_per_fragment\n", - peasycap->audio_bytes_per_fragment); - JOM(4, "%6i=audio_buffer_page_many\n", - peasycap->audio_buffer_page_many); - - peasycap->audio_isoc_framesperdesc = AUDIO_ISOC_FRAMESPERDESC; - - JOM(4, "%i=audio_isoc_framesperdesc\n", - peasycap->audio_isoc_framesperdesc); - if (0 >= peasycap->audio_isoc_framesperdesc) { - SAM("ERROR: bad audio_isoc_framesperdesc\n"); - return -ENOENT; - } - - peasycap->audio_isoc_buffer_size = - peasycap->audio_isoc_maxframesize * - peasycap->audio_isoc_framesperdesc; - JOM(4, "%i=audio_isoc_buffer_size\n", - peasycap->audio_isoc_buffer_size); - if (AUDIO_ISOC_BUFFER_SIZE < peasycap->audio_isoc_buffer_size) { - SAM("MISTAKE: audio_isoc_buffer_size bigger " - "than %li=AUDIO_ISOC_BUFFER_SIZE\n", - AUDIO_ISOC_BUFFER_SIZE); - return -EFAULT; - } - if (-1 == peasycap->audio_interface) { - SAM("MISTAKE: audio_interface is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->audio_altsetting_on) { - SAM("MISTAKE: audio_altsetting_on is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->audio_altsetting_off) { - SAM("MISTAKE: audio_interface_off is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->audio_endpointnumber) { - SAM("MISTAKE: audio_endpointnumber is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->audio_isoc_maxframesize) { - SAM("MISTAKE: audio_isoc_maxframesize is unset\n"); - return -EFAULT; - } - if (-1 == peasycap->audio_isoc_buffer_size) { - SAM("MISTAKE: audio_isoc_buffer_size is unset\n"); - return -EFAULT; - } -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST. - */ -/*---------------------------------------------------------------------------*/ - INIT_LIST_HEAD(&(peasycap->urb_audio_head)); - peasycap->purb_audio_head = &(peasycap->urb_audio_head); - -/*---------------------------------------------------------------------------*/ - JOM(4, "allocating %i isoc audio buffers of size %i\n", - AUDIO_ISOC_BUFFER_MANY, - peasycap->audio_isoc_buffer_size); - JOM(4, ".... each occupying contiguous memory pages\n"); - - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - pbuf = (void *)__get_free_pages(GFP_KERNEL, - AUDIO_ISOC_ORDER); - if (!pbuf) { - SAM("ERROR: Could not allocate isoc audio buffer " - "%i\n", k); - return -ENOMEM; - } else - peasycap->allocation_audio_page += - BIT(AUDIO_ISOC_ORDER); - - peasycap->audio_isoc_buffer[k].pgo = pbuf; - peasycap->audio_isoc_buffer[k].pto = pbuf + - peasycap->audio_isoc_buffer_size; - peasycap->audio_isoc_buffer[k].kount = k; - } - JOM(4, "allocation of isoc audio buffers done.\n"); -/*---------------------------------------------------------------------------*/ -/* - * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... - */ -/*---------------------------------------------------------------------------*/ - JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); - JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n", - peasycap->audio_isoc_maxframesize); - JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n", - peasycap->audio_isoc_buffer_size); - - for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { - purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc, - GFP_KERNEL); - if (!purb) { - SAM("ERROR: usb_alloc_urb returned NULL for buffer " - "%i\n", k); - return -ENOMEM; - } - peasycap->allocation_audio_urb += 1 ; -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); - if (!pdata_urb) { - SAM("ERROR: Could not allocate struct data_urb.\n"); - return -ENOMEM; - } - peasycap->allocation_audio_struct += - sizeof(struct data_urb); - - pdata_urb->purb = purb; - pdata_urb->isbuf = k; - pdata_urb->length = 0; - list_add_tail(&(pdata_urb->list_head), - peasycap->purb_audio_head); -/*---------------------------------------------------------------------------*/ -/* - * ... AND INITIALIZE THEM - */ -/*---------------------------------------------------------------------------*/ - if (!k) { - JOM(4, "initializing audio urbs thus:\n"); - JOM(4, " purb->interval = 1;\n"); - JOM(4, " purb->dev = peasycap->pusb_device;\n"); - JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->" - "pusb_device,%i);\n", - peasycap->audio_endpointnumber); - JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); - JOM(4, " purb->transfer_buffer = " - "peasycap->audio_isoc_buffer[.].pgo;\n"); - JOM(4, " purb->transfer_buffer_length = %i;\n", - peasycap->audio_isoc_buffer_size); - JOM(4, " purb->complete = easycap_alsa_complete;\n"); - JOM(4, " purb->context = peasycap;\n"); - JOM(4, " purb->start_frame = 0;\n"); - JOM(4, " purb->number_of_packets = %i;\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, " for (j = 0; j < %i; j++)\n", - peasycap->audio_isoc_framesperdesc); - JOM(4, " {\n"); - JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", - peasycap->audio_isoc_maxframesize); - JOM(4, " purb->iso_frame_desc[j].length = %i;\n", - peasycap->audio_isoc_maxframesize); - JOM(4, " }\n"); - } - - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, - peasycap->audio_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo; - purb->transfer_buffer_length = - peasycap->audio_isoc_buffer_size; - purb->complete = easycap_alsa_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = peasycap->audio_isoc_framesperdesc; - for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j].offset = j * - peasycap->audio_isoc_maxframesize; - purb->iso_frame_desc[j].length = - peasycap->audio_isoc_maxframesize; - } - } - JOM(4, "allocation of %i struct urb done.\n", k); -/*---------------------------------------------------------------------------*/ -/* - * SAVE POINTER peasycap IN THIS INTERFACE. - */ -/*---------------------------------------------------------------------------*/ - usb_set_intfdata(intf, peasycap); -/*---------------------------------------------------------------------------*/ -/* - * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. - */ -/*---------------------------------------------------------------------------*/ - JOM(4, "initializing ALSA card\n"); - - rc = easycap_alsa_probe(peasycap); - if (rc) { - err("easycap_alsa_probe() rc = %i\n", rc); - return -ENODEV; - } - - - JOM(8, "kref_get() with %i=kref.refcount.counter\n", - peasycap->kref.refcount.counter); - kref_get(&peasycap->kref); - peasycap->registered_audio++; - break; - } -/*---------------------------------------------------------------------------*/ -/* - * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED - */ -/*---------------------------------------------------------------------------*/ - default: - JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber); - return -EINVAL; - } - SAM("ends successfully for interface %i\n", bInterfaceNumber); - return 0; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY - * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID. - * - * THIS FUNCTION AFFECTS ALSA. BEWARE. - */ -/*---------------------------------------------------------------------------*/ -static void easycap_usb_disconnect(struct usb_interface *pusb_interface) -{ - struct usb_host_interface *pusb_host_interface; - struct usb_interface_descriptor *pusb_interface_descriptor; - u8 bInterfaceNumber; - struct easycap *peasycap; - - struct list_head *plist_head; - struct data_urb *pdata_urb; - int minor, m, kd; - - JOT(4, "\n"); - - pusb_host_interface = pusb_interface->cur_altsetting; - if (!pusb_host_interface) { - JOT(4, "ERROR: pusb_host_interface is NULL\n"); - return; - } - pusb_interface_descriptor = &(pusb_host_interface->desc); - if (!pusb_interface_descriptor) { - JOT(4, "ERROR: pusb_interface_descriptor is NULL\n"); - return; - } - bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber; - minor = pusb_interface->minor; - JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor); - - if (1 == bInterfaceNumber) - return; - - peasycap = usb_get_intfdata(pusb_interface); - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return; - } -/*---------------------------------------------------------------------------*/ -/* - * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE. -*/ -/*---------------------------------------------------------------------------*/ - peasycap->video_eof = 1; - peasycap->audio_eof = 1; - wake_up_interruptible(&(peasycap->wq_video)); - wake_up_interruptible(&(peasycap->wq_audio)); -/*---------------------------------------------------------------------------*/ - switch (bInterfaceNumber) { - case 0: { - if (peasycap->purb_video_head) { - JOM(4, "killing video urbs\n"); - m = 0; - list_for_each(plist_head, peasycap->purb_video_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - if (pdata_urb->purb) { - usb_kill_urb(pdata_urb->purb); - m++; - } - } - } - JOM(4, "%i video urbs killed\n", m); - } - break; - } -/*---------------------------------------------------------------------------*/ - case 2: { - if (peasycap->purb_audio_head) { - JOM(4, "killing audio urbs\n"); - m = 0; - list_for_each(plist_head, peasycap->purb_audio_head) { - pdata_urb = list_entry(plist_head, - struct data_urb, list_head); - if (pdata_urb) { - if (pdata_urb->purb) { - usb_kill_urb(pdata_urb->purb); - m++; - } - } - } - JOM(4, "%i audio urbs killed\n", m); - } - break; - } - default: - break; - } -/*--------------------------------------------------------------------------*/ -/* - * DEREGISTER - * - * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO - * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN - * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE. - */ -/*--------------------------------------------------------------------------*/ - kd = isdongle(peasycap); - switch (bInterfaceNumber) { - case 0: { - if (0 <= kd && DONGLE_MANY > kd) { - wake_up_interruptible(&peasycap->wq_video); - JOM(4, "about to lock dongle[%i].mutex_video\n", kd); - if (mutex_lock_interruptible(&easycapdc60_dongle[kd]. - mutex_video)) { - SAY("ERROR: " - "cannot lock dongle[%i].mutex_video\n", kd); - return; - } - JOM(4, "locked dongle[%i].mutex_video\n", kd); - } else { - SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); - } -/*---------------------------------------------------------------------------*/ - if (!peasycap->v4l2_device.name[0]) { - SAM("ERROR: peasycap->v4l2_device.name is empty\n"); - if (0 <= kd && DONGLE_MANY > kd) - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - return; - } - v4l2_device_disconnect(&peasycap->v4l2_device); - JOM(4, "v4l2_device_disconnect() OK\n"); - v4l2_device_unregister(&peasycap->v4l2_device); - JOM(4, "v4l2_device_unregister() OK\n"); - - video_unregister_device(&peasycap->video_device); - JOM(4, "intf[%i]: video_unregister_device() minor=%i\n", - bInterfaceNumber, minor); - peasycap->registered_video--; -/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ - - if (0 <= kd && DONGLE_MANY > kd) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - JOM(4, "unlocked dongle[%i].mutex_video\n", kd); - } - break; - } - case 2: { - if (0 <= kd && DONGLE_MANY > kd) { - wake_up_interruptible(&peasycap->wq_audio); - JOM(4, "about to lock dongle[%i].mutex_audio\n", kd); - if (mutex_lock_interruptible(&easycapdc60_dongle[kd]. - mutex_audio)) { - SAY("ERROR: " - "cannot lock dongle[%i].mutex_audio\n", kd); - return; - } - JOM(4, "locked dongle[%i].mutex_audio\n", kd); - } else - SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); - if (0 != snd_card_free(peasycap->psnd_card)) { - SAY("ERROR: snd_card_free() failed\n"); - } else { - peasycap->psnd_card = NULL; - (peasycap->registered_audio)--; - } - if (0 <= kd && DONGLE_MANY > kd) { - mutex_unlock(&easycapdc60_dongle[kd].mutex_audio); - JOM(4, "unlocked dongle[%i].mutex_audio\n", kd); - } - break; - } - default: - break; - } -/*---------------------------------------------------------------------------*/ -/* - * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap - * (ALSO WHEN ALSA HAS BEEN IN USE) - */ -/*---------------------------------------------------------------------------*/ - if (!peasycap->kref.refcount.counter) { - SAM("ERROR: peasycap->kref.refcount.counter is zero " - "so cannot call kref_put()\n"); - SAM("ending unsuccessfully: may cause memory leak\n"); - return; - } - if (0 <= kd && DONGLE_MANY > kd) { - JOM(4, "about to lock dongle[%i].mutex_video\n", kd); - if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { - SAY("ERROR: cannot lock dongle[%i].mutex_video\n", kd); - SAM("ending unsuccessfully: may cause memory leak\n"); - return; - } - JOM(4, "locked dongle[%i].mutex_video\n", kd); - JOM(4, "about to lock dongle[%i].mutex_audio\n", kd); - if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_audio)) { - SAY("ERROR: cannot lock dongle[%i].mutex_audio\n", kd); - mutex_unlock(&(easycapdc60_dongle[kd].mutex_video)); - JOM(4, "unlocked dongle[%i].mutex_video\n", kd); - SAM("ending unsuccessfully: may cause memory leak\n"); - return; - } - JOM(4, "locked dongle[%i].mutex_audio\n", kd); - } - JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n", - bInterfaceNumber, (int)peasycap->kref.refcount.counter); - kref_put(&peasycap->kref, easycap_delete); - JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber); - if (0 <= kd && DONGLE_MANY > kd) { - mutex_unlock(&(easycapdc60_dongle[kd].mutex_audio)); - JOT(4, "unlocked dongle[%i].mutex_audio\n", kd); - mutex_unlock(&easycapdc60_dongle[kd].mutex_video); - JOT(4, "unlocked dongle[%i].mutex_video\n", kd); - } -/*---------------------------------------------------------------------------*/ - JOM(4, "ends\n"); - return; -} -/*****************************************************************************/ - -/*---------------------------------------------------------------------------*/ -/* - * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO - */ -/*---------------------------------------------------------------------------*/ -static struct usb_device_id easycap_usb_device_id_table[] = { - {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)}, - { } -}; - -MODULE_DEVICE_TABLE(usb, easycap_usb_device_id_table); -struct usb_driver easycap_usb_driver = { - .name = "easycap", - .id_table = easycap_usb_device_id_table, - .probe = easycap_usb_probe, - .disconnect = easycap_usb_disconnect, -}; - -static int __init easycap_module_init(void) -{ - int k, rc; - - printk(KERN_INFO "Easycap version: "EASYCAP_DRIVER_VERSION "\n"); - - JOT(4, "begins. %i=debug %i=bars %i=gain\n", - easycap_debug, easycap_bars, easycap_gain); - - mutex_init(&mutex_dongle); - for (k = 0; k < DONGLE_MANY; k++) { - easycapdc60_dongle[k].peasycap = NULL; - mutex_init(&easycapdc60_dongle[k].mutex_video); - mutex_init(&easycapdc60_dongle[k].mutex_audio); - } - rc = usb_register(&easycap_usb_driver); - if (rc) - printk(KERN_ERR "Easycap: usb_register failed rc=%d\n", rc); - - return rc; -} -/*****************************************************************************/ -static void __exit easycap_module_exit(void) -{ - usb_deregister(&easycap_usb_driver); -} -/*****************************************************************************/ - -module_init(easycap_module_init); -module_exit(easycap_module_exit); - -/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_settings.c b/drivers/staging/easycap/easycap_settings.c deleted file mode 100644 index 70f59b13c34d..000000000000 --- a/drivers/staging/easycap/easycap_settings.c +++ /dev/null @@ -1,696 +0,0 @@ -/****************************************************************************** -* * -* easycap_settings.c * -* * -******************************************************************************/ -/* - * - * Copyright (C) 2010 R.M. Thomas - * - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ -/*****************************************************************************/ - -#include "easycap.h" - -/*---------------------------------------------------------------------------*/ -/* - * THE LEAST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING: - * 0 => 25 fps - * 1 => 30 fps - * - * THE MOST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING: - * 0 => full framerate - * 1 => 20% framerate - */ -/*---------------------------------------------------------------------------*/ -const struct easycap_standard easycap_standard[] = { - { - .mask = 0x00FF & PAL_BGHIN , - .v4l2_standard = { - .index = PAL_BGHIN, - .id = (V4L2_STD_PAL_B | - V4L2_STD_PAL_G | V4L2_STD_PAL_H | - V4L2_STD_PAL_I | V4L2_STD_PAL_N), - .name = "PAL_BGHIN", - .frameperiod = {1, 25}, - .framelines = 625, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & NTSC_N_443 , - .v4l2_standard = { - .index = NTSC_N_443, - .id = V4L2_STD_UNKNOWN, - .name = "NTSC_N_443", - .frameperiod = {1, 25}, - .framelines = 480, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & PAL_Nc , - .v4l2_standard = { - .index = PAL_Nc, - .id = V4L2_STD_PAL_Nc, - .name = "PAL_Nc", - .frameperiod = {1, 25}, - .framelines = 625, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & NTSC_N , - .v4l2_standard = { - .index = NTSC_N, - .id = V4L2_STD_UNKNOWN, - .name = "NTSC_N", - .frameperiod = {1, 25}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & SECAM , - .v4l2_standard = { - .index = SECAM, - .id = V4L2_STD_SECAM, - .name = "SECAM", - .frameperiod = {1, 25}, - .framelines = 625, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & NTSC_M , - .v4l2_standard = { - .index = NTSC_M, - .id = V4L2_STD_NTSC_M, - .name = "NTSC_M", - .frameperiod = {1, 30}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & NTSC_M_JP , - .v4l2_standard = { - .index = NTSC_M_JP, - .id = V4L2_STD_NTSC_M_JP, - .name = "NTSC_M_JP", - .frameperiod = {1, 30}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & PAL_60 , - .v4l2_standard = { - .index = PAL_60, - .id = V4L2_STD_PAL_60, - .name = "PAL_60", - .frameperiod = {1, 30}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & NTSC_443 , - .v4l2_standard = { - .index = NTSC_443, - .id = V4L2_STD_NTSC_443, - .name = "NTSC_443", - .frameperiod = {1, 30}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x00FF & PAL_M , - .v4l2_standard = { - .index = PAL_M, - .id = V4L2_STD_PAL_M, - .name = "PAL_M", - .frameperiod = {1, 30}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & PAL_BGHIN_SLOW), - .v4l2_standard = { - .index = PAL_BGHIN_SLOW, - .id = (V4L2_STD_PAL_B | V4L2_STD_PAL_G | - V4L2_STD_PAL_H | - V4L2_STD_PAL_I | V4L2_STD_PAL_N | - (((v4l2_std_id)0x01) << 32)), - .name = "PAL_BGHIN_SLOW", - .frameperiod = {1, 5}, - .framelines = 625, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & NTSC_N_443_SLOW), - .v4l2_standard = { - .index = NTSC_N_443_SLOW, - .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x11) << 32)), - .name = "NTSC_N_443_SLOW", - .frameperiod = {1, 5}, - .framelines = 480, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & PAL_Nc_SLOW), - .v4l2_standard = { - .index = PAL_Nc_SLOW, - .id = (V4L2_STD_PAL_Nc | (((v4l2_std_id)0x01) << 32)), - .name = "PAL_Nc_SLOW", - .frameperiod = {1, 5}, - .framelines = 625, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & NTSC_N_SLOW), - .v4l2_standard = { - .index = NTSC_N_SLOW, - .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x21) << 32)), - .name = "NTSC_N_SLOW", - .frameperiod = {1, 5}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & SECAM_SLOW), - .v4l2_standard = { - .index = SECAM_SLOW, - .id = (V4L2_STD_SECAM | (((v4l2_std_id)0x01) << 32)), - .name = "SECAM_SLOW", - .frameperiod = {1, 5}, - .framelines = 625, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & NTSC_M_SLOW), - .v4l2_standard = { - .index = NTSC_M_SLOW, - .id = (V4L2_STD_NTSC_M | (((v4l2_std_id)0x01) << 32)), - .name = "NTSC_M_SLOW", - .frameperiod = {1, 6}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & NTSC_M_JP_SLOW), - .v4l2_standard = { - .index = NTSC_M_JP_SLOW, - .id = (V4L2_STD_NTSC_M_JP | - (((v4l2_std_id)0x01) << 32)), - .name = "NTSC_M_JP_SLOW", - .frameperiod = {1, 6}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & PAL_60_SLOW), - .v4l2_standard = { - .index = PAL_60_SLOW, - .id = (V4L2_STD_PAL_60 | (((v4l2_std_id)0x01) << 32)), - .name = "PAL_60_SLOW", - .frameperiod = {1, 6}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & NTSC_443_SLOW), - .v4l2_standard = { - .index = NTSC_443_SLOW, - .id = (V4L2_STD_NTSC_443 | (((v4l2_std_id)0x01) << 32)), - .name = "NTSC_443_SLOW", - .frameperiod = {1, 6}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0x8000 | (0x00FF & PAL_M_SLOW), - .v4l2_standard = { - .index = PAL_M_SLOW, - .id = (V4L2_STD_PAL_M | (((v4l2_std_id)0x01) << 32)), - .name = "PAL_M_SLOW", - .frameperiod = {1, 6}, - .framelines = 525, - .reserved = {0, 0, 0, 0} - } - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .mask = 0xFFFF - } -}; -/*---------------------------------------------------------------------------*/ -/* - * THE 16-BIT easycap_format.mask HAS MEANING: - * (least significant) BIT 0: 0 => PAL, 25 FPS; 1 => NTSC, 30 FPS - * BITS 2-4: RESERVED FOR DIFFERENTIATING STANDARDS - * BITS 5-7: NUMBER OF BYTES PER PIXEL - * BIT 8: 0 => NATIVE BYTE ORDER; 1 => SWAPPED - * BITS 9-10: RESERVED FOR OTHER BYTE PERMUTATIONS - * BIT 11: 0 => UNDECIMATED; 1 => DECIMATED - * BIT 12: 0 => OFFER FRAMES; 1 => OFFER FIELDS - * BIT 13: 0 => FULL FRAMERATE; 1 => REDUCED - * (most significant) BITS 14-15: RESERVED FOR OTHER FIELD/FRAME OPTIONS - * IT FOLLOWS THAT: - * bytesperpixel IS ((0x00E0 & easycap_format.mask) >> 5) - * byteswaporder IS true IF (0 != (0x0100 & easycap_format.mask)) - * - * decimatepixel IS true IF (0 != (0x0800 & easycap_format.mask)) - * - * offerfields IS true IF (0 != (0x1000 & easycap_format.mask)) - */ -/*---------------------------------------------------------------------------*/ - -struct easycap_format easycap_format[1 + SETTINGS_MANY]; - -int fillin_formats(void) -{ - const char *name1, *name2, *name3, *name4; - struct v4l2_format *fmt; - int i, j, k, m, n; - u32 width, height, pixelformat, bytesperline, sizeimage; - u16 mask1, mask2, mask3, mask4; - enum v4l2_field field; - enum v4l2_colorspace colorspace; - - for (i = 0, n = 0; i < STANDARD_MANY; i++) { - mask1 = 0x0000; - switch (i) { - case PAL_BGHIN: { - mask1 = 0x1F & PAL_BGHIN; - name1 = "PAL_BGHIN"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case SECAM: { - mask1 = 0x1F & SECAM; - name1 = "SECAM"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case PAL_Nc: { - mask1 = 0x1F & PAL_Nc; - name1 = "PAL_Nc"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case PAL_60: { - mask1 = 0x1F & PAL_60; - name1 = "PAL_60"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case PAL_M: { - mask1 = 0x1F & PAL_M; - name1 = "PAL_M"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case NTSC_M: { - mask1 = 0x1F & NTSC_M; - name1 = "NTSC_M"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_443: { - mask1 = 0x1F & NTSC_443; - name1 = "NTSC_443"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_M_JP: { - mask1 = 0x1F & NTSC_M_JP; - name1 = "NTSC_M_JP"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_N: { - mask1 = 0x1F & NTSC_M; - name1 = "NTSC_N"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_N_443: { - mask1 = 0x1F & NTSC_N_443; - name1 = "NTSC_N_443"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case PAL_BGHIN_SLOW: { - mask1 = 0x001F & PAL_BGHIN_SLOW; - mask1 |= 0x0200; - name1 = "PAL_BGHIN_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case SECAM_SLOW: { - mask1 = 0x001F & SECAM_SLOW; - mask1 |= 0x0200; - name1 = "SECAM_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case PAL_Nc_SLOW: { - mask1 = 0x001F & PAL_Nc_SLOW; - mask1 |= 0x0200; - name1 = "PAL_Nc_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case PAL_60_SLOW: { - mask1 = 0x001F & PAL_60_SLOW; - mask1 |= 0x0200; - name1 = "PAL_60_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case PAL_M_SLOW: { - mask1 = 0x001F & PAL_M_SLOW; - mask1 |= 0x0200; - name1 = "PAL_M_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; - break; - } - case NTSC_M_SLOW: { - mask1 = 0x001F & NTSC_M_SLOW; - mask1 |= 0x0200; - name1 = "NTSC_M_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_443_SLOW: { - mask1 = 0x001F & NTSC_443_SLOW; - mask1 |= 0x0200; - name1 = "NTSC_443_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_M_JP_SLOW: { - mask1 = 0x001F & NTSC_M_JP_SLOW; - mask1 |= 0x0200; - name1 = "NTSC_M_JP_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_N_SLOW: { - mask1 = 0x001F & NTSC_N_SLOW; - mask1 |= 0x0200; - name1 = "NTSC_N_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - case NTSC_N_443_SLOW: { - mask1 = 0x001F & NTSC_N_443_SLOW; - mask1 |= 0x0200; - name1 = "NTSC_N_443_SLOW"; - colorspace = V4L2_COLORSPACE_470_SYSTEM_M; - break; - } - default: - return -1; - } - - for (j = 0; j < RESOLUTION_MANY; j++) { - mask2 = 0x0000; - switch (j) { - case AT_720x576: { - if (0x1 & mask1) - continue; - name2 = "_AT_720x576"; - width = 720; - height = 576; - break; - } - case AT_704x576: { - if (0x1 & mask1) - continue; - name2 = "_AT_704x576"; - width = 704; - height = 576; - break; - } - case AT_640x480: { - name2 = "_AT_640x480"; - width = 640; - height = 480; - break; - } - case AT_720x480: { - if (!(0x1 & mask1)) - continue; - name2 = "_AT_720x480"; - width = 720; - height = 480; - break; - } - case AT_360x288: { - if (0x1 & mask1) - continue; - name2 = "_AT_360x288"; - width = 360; - height = 288; - mask2 = 0x0800; - break; - } - case AT_320x240: { - name2 = "_AT_320x240"; - width = 320; - height = 240; - mask2 = 0x0800; - break; - } - case AT_360x240: { - if (!(0x1 & mask1)) - continue; - name2 = "_AT_360x240"; - width = 360; - height = 240; - mask2 = 0x0800; - break; - } - default: - return -2; - } - - for (k = 0; k < PIXELFORMAT_MANY; k++) { - mask3 = 0x0000; - switch (k) { - case FMT_UYVY: { - name3 = __stringify(FMT_UYVY); - pixelformat = V4L2_PIX_FMT_UYVY; - mask3 |= (0x02 << 5); - break; - } - case FMT_YUY2: { - name3 = __stringify(FMT_YUY2); - pixelformat = V4L2_PIX_FMT_YUYV; - mask3 |= (0x02 << 5); - mask3 |= 0x0100; - break; - } - case FMT_RGB24: { - name3 = __stringify(FMT_RGB24); - pixelformat = V4L2_PIX_FMT_RGB24; - mask3 |= (0x03 << 5); - break; - } - case FMT_RGB32: { - name3 = __stringify(FMT_RGB32); - pixelformat = V4L2_PIX_FMT_RGB32; - mask3 |= (0x04 << 5); - break; - } - case FMT_BGR24: { - name3 = __stringify(FMT_BGR24); - pixelformat = V4L2_PIX_FMT_BGR24; - mask3 |= (0x03 << 5); - mask3 |= 0x0100; - break; - } - case FMT_BGR32: { - name3 = __stringify(FMT_BGR32); - pixelformat = V4L2_PIX_FMT_BGR32; - mask3 |= (0x04 << 5); - mask3 |= 0x0100; - break; - } - default: - return -3; - } - bytesperline = width * ((mask3 & 0x00E0) >> 5); - sizeimage = bytesperline * height; - - for (m = 0; m < INTERLACE_MANY; m++) { - mask4 = 0x0000; - switch (m) { - case FIELD_NONE: { - name4 = "-n"; - field = V4L2_FIELD_NONE; - break; - } - case FIELD_INTERLACED: { - name4 = "-i"; - mask4 |= 0x1000; - field = V4L2_FIELD_INTERLACED; - break; - } - default: - return -4; - } - if (SETTINGS_MANY <= n) - return -5; - - strcpy(easycap_format[n].name, name1); - strcat(easycap_format[n].name, name2); - strcat(easycap_format[n].name, "_"); - strcat(easycap_format[n].name, name3); - strcat(easycap_format[n].name, name4); - easycap_format[n].mask = - mask1 | mask2 | mask3 | mask4; - fmt = &easycap_format[n].v4l2_format; - - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->fmt.pix.width = width; - fmt->fmt.pix.height = height; - fmt->fmt.pix.pixelformat = pixelformat; - fmt->fmt.pix.field = field; - fmt->fmt.pix.bytesperline = bytesperline; - fmt->fmt.pix.sizeimage = sizeimage; - fmt->fmt.pix.colorspace = colorspace; - fmt->fmt.pix.priv = 0; - n++; - } - } - } - } - if ((1 + SETTINGS_MANY) <= n) - return -6; - easycap_format[n].mask = 0xFFFF; - return n; -} -/*---------------------------------------------------------------------------*/ -struct v4l2_queryctrl easycap_control[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = SAA_0A_DEFAULT, - .flags = 0, - .reserved = {0, 0} - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .id = V4L2_CID_CONTRAST, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Contrast", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = SAA_0B_DEFAULT + 128, - .flags = 0, - .reserved = {0, 0} - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .id = V4L2_CID_SATURATION, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Saturation", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = SAA_0C_DEFAULT + 128, - .flags = 0, - .reserved = {0, 0} - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .id = V4L2_CID_HUE, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Hue", - .minimum = 0, - .maximum = 255, - .step = 1, - .default_value = SAA_0D_DEFAULT + 128, - .flags = 0, - .reserved = {0, 0} - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .id = V4L2_CID_AUDIO_VOLUME, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Volume", - .minimum = 0, - .maximum = 31, - .step = 1, - .default_value = 16, - .flags = 0, - .reserved = {0, 0} - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .id = V4L2_CID_AUDIO_MUTE, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Mute", - .default_value = true, - .flags = 0, - .reserved = {0, 0} - }, -/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ - { - .id = 0xFFFFFFFF - } -}; -/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_sound.c b/drivers/staging/easycap/easycap_sound.c deleted file mode 100644 index b22bb39b5f69..000000000000 --- a/drivers/staging/easycap/easycap_sound.c +++ /dev/null @@ -1,816 +0,0 @@ -/****************************************************************************** -* * -* easycap_sound.c * -* * -* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 * -* * -* * -******************************************************************************/ -/* - * - * Copyright (C) 2010 R.M. Thomas - * - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ -/*****************************************************************************/ - -#include "easycap.h" - -/*--------------------------------------------------------------------------*/ -/* - * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE - */ -/*--------------------------------------------------------------------------*/ -static const struct snd_pcm_hardware alsa_hardware = { - .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_32000 | SNDRV_PCM_RATE_48000, - .rate_min = 32000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = PAGE_SIZE * - PAGES_PER_AUDIO_FRAGMENT * - AUDIO_FRAGMENT_MANY, - .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT, - .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2, - .periods_min = AUDIO_FRAGMENT_MANY, - .periods_max = AUDIO_FRAGMENT_MANY * 2, -}; - - -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER - * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE, - * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO. - */ -/*---------------------------------------------------------------------------*/ -void -easycap_alsa_complete(struct urb *purb) -{ - struct easycap *peasycap; - struct snd_pcm_substream *pss; - struct snd_pcm_runtime *prt; - int dma_bytes, fragment_bytes; - int isfragment; - u8 *p1, *p2; - s16 tmp; - int i, j, more, much, rc; -#ifdef UPSAMPLE - int k; - s16 oldaudio, newaudio, delta; -#endif /*UPSAMPLE*/ - - JOT(16, "\n"); - - if (!purb) { - SAY("ERROR: purb is NULL\n"); - return; - } - peasycap = purb->context; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return; - } - much = 0; - if (peasycap->audio_idle) { - JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n", - peasycap->audio_idle, peasycap->audio_isoc_streaming); - if (peasycap->audio_isoc_streaming) - goto resubmit; - } -/*---------------------------------------------------------------------------*/ - pss = peasycap->psubstream; - if (!pss) - goto resubmit; - prt = pss->runtime; - if (!prt) - goto resubmit; - dma_bytes = (int)prt->dma_bytes; - if (0 == dma_bytes) - goto resubmit; - fragment_bytes = 4 * ((int)prt->period_size); - if (0 == fragment_bytes) - goto resubmit; -/* -------------------------------------------------------------------------*/ - if (purb->status) { - if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) { - JOM(16, "urb status -ESHUTDOWN or -ENOENT\n"); - return; - } - SAM("ERROR: non-zero urb status: -%s: %d\n", - strerror(purb->status), purb->status); - goto resubmit; - } -/*---------------------------------------------------------------------------*/ -/* - * PROCEED HERE WHEN NO ERROR - */ -/*---------------------------------------------------------------------------*/ - -#ifdef UPSAMPLE - oldaudio = peasycap->oldaudio; -#endif /*UPSAMPLE*/ - - for (i = 0; i < purb->number_of_packets; i++) { - if (purb->iso_frame_desc[i].status < 0) { - SAM("-%s: %d\n", - strerror(purb->iso_frame_desc[i].status), - purb->iso_frame_desc[i].status); - } - if (purb->iso_frame_desc[i].status) { - JOM(12, "discarding audio samples because " - "%i=purb->iso_frame_desc[i].status\n", - purb->iso_frame_desc[i].status); - continue; - } - more = purb->iso_frame_desc[i].actual_length; - if (more == 0) { - peasycap->audio_mt++; - continue; - } - if (0 > more) { - SAM("MISTAKE: more is negative\n"); - return; - } - - if (peasycap->audio_mt) { - JOM(12, "%4i empty audio urb frames\n", - peasycap->audio_mt); - peasycap->audio_mt = 0; - } - - p1 = (u8 *)(purb->transfer_buffer + - purb->iso_frame_desc[i].offset); - - /* - * COPY more BYTES FROM ISOC BUFFER - * TO THE DMA BUFFER, CONVERTING - * 8-BIT MONO TO 16-BIT SIGNED - * LITTLE-ENDIAN SAMPLES IF NECESSARY - */ - while (more) { - much = dma_bytes - peasycap->dma_fill; - if (0 > much) { - SAM("MISTAKE: much is negative\n"); - return; - } - if (0 == much) { - peasycap->dma_fill = 0; - peasycap->dma_next = fragment_bytes; - JOM(8, "wrapped dma buffer\n"); - } - if (!peasycap->microphone) { - if (much > more) - much = more; - memcpy(prt->dma_area + peasycap->dma_fill, - p1, much); - p1 += much; - more -= much; - } else { -#ifdef UPSAMPLE - if (much % 16) - JOM(8, "MISTAKE? much" - " is not divisible by 16\n"); - if (much > (16 * more)) - much = 16 * more; - p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); - - for (j = 0; j < (much / 16); j++) { - newaudio = ((int) *p1) - 128; - newaudio = 128 * newaudio; - - delta = (newaudio - oldaudio) / 4; - tmp = oldaudio + delta; - - for (k = 0; k < 4; k++) { - *p2 = (0x00FF & tmp); - *(p2 + 1) = (0xFF00 & tmp) >> 8; - p2 += 2; - *p2 = (0x00FF & tmp); - *(p2 + 1) = (0xFF00 & tmp) >> 8; - p2 += 2; - tmp += delta; - } - p1++; - more--; - oldaudio = tmp; - } -#else /*!UPSAMPLE*/ - if (much > (2 * more)) - much = 2 * more; - p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); - - for (j = 0; j < (much / 2); j++) { - tmp = ((int) *p1) - 128; - tmp = 128 * tmp; - *p2 = (0x00FF & tmp); - *(p2 + 1) = (0xFF00 & tmp) >> 8; - p1++; - p2 += 2; - more--; - } -#endif /*UPSAMPLE*/ - } - peasycap->dma_fill += much; - if (peasycap->dma_fill >= peasycap->dma_next) { - isfragment = peasycap->dma_fill / fragment_bytes; - if (0 > isfragment) { - SAM("MISTAKE: isfragment is negative\n"); - return; - } - peasycap->dma_read = (isfragment - 1) * fragment_bytes; - peasycap->dma_next = (isfragment + 1) * fragment_bytes; - if (dma_bytes < peasycap->dma_next) - peasycap->dma_next = fragment_bytes; - - if (0 <= peasycap->dma_read) { - JOM(8, "snd_pcm_period_elapsed(), %i=" - "isfragment\n", isfragment); - snd_pcm_period_elapsed(pss); - } - } - } - -#ifdef UPSAMPLE - peasycap->oldaudio = oldaudio; -#endif /*UPSAMPLE*/ - - } -/*---------------------------------------------------------------------------*/ -/* - * RESUBMIT THIS URB - */ -/*---------------------------------------------------------------------------*/ -resubmit: - if (peasycap->audio_isoc_streaming == 0) - return; - - rc = usb_submit_urb(purb, GFP_ATOMIC); - if (rc) { - if ((-ENODEV != rc) && (-ENOENT != rc)) { - SAM("ERROR: while %i=audio_idle, usb_submit_urb failed " - "with rc: -%s :%d\n", - peasycap->audio_idle, strerror(rc), rc); - } - if (0 < peasycap->audio_isoc_streaming) - peasycap->audio_isoc_streaming--; - } - return; -} -/*****************************************************************************/ -static int easycap_alsa_open(struct snd_pcm_substream *pss) -{ - struct snd_pcm *psnd_pcm; - struct snd_card *psnd_card; - struct easycap *peasycap; - - JOT(4, "\n"); - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - psnd_pcm = pss->pcm; - if (!psnd_pcm) { - SAY("ERROR: psnd_pcm is NULL\n"); - return -EFAULT; - } - psnd_card = psnd_pcm->card; - if (!psnd_card) { - SAY("ERROR: psnd_card is NULL\n"); - return -EFAULT; - } - - peasycap = psnd_card->private_data; - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (peasycap->psnd_card != psnd_card) { - SAM("ERROR: bad peasycap->psnd_card\n"); - return -EFAULT; - } - if (peasycap->psubstream) { - SAM("ERROR: bad peasycap->psubstream\n"); - return -EFAULT; - } - pss->private_data = peasycap; - peasycap->psubstream = pss; - pss->runtime->hw = peasycap->alsa_hardware; - pss->runtime->private_data = peasycap; - pss->private_data = peasycap; - - if (0 != easycap_sound_setup(peasycap)) { - JOM(4, "ending unsuccessfully\n"); - return -EFAULT; - } - JOM(4, "ending successfully\n"); - return 0; -} -/*****************************************************************************/ -static int easycap_alsa_close(struct snd_pcm_substream *pss) -{ - struct easycap *peasycap; - - JOT(4, "\n"); - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - peasycap = snd_pcm_substream_chip(pss); - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - pss->private_data = NULL; - peasycap->psubstream = NULL; - JOT(4, "ending successfully\n"); - return 0; -} -/*****************************************************************************/ -static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz) -{ - struct snd_pcm_runtime *prt; - JOT(4, "\n"); - - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - prt = pss->runtime; - if (!prt) { - SAY("ERROR: substream.runtime is NULL\n"); - return -EFAULT; - } - if (prt->dma_area) { - if (prt->dma_bytes > sz) - return 0; - vfree(prt->dma_area); - } - prt->dma_area = vmalloc(sz); - if (!prt->dma_area) - return -ENOMEM; - prt->dma_bytes = sz; - return 0; -} -/*****************************************************************************/ -static int easycap_alsa_hw_params(struct snd_pcm_substream *pss, - struct snd_pcm_hw_params *phw) -{ - int rc; - - JOT(4, "%i\n", (params_buffer_bytes(phw))); - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw)); - if (rc) - return rc; - return 0; -} -/*****************************************************************************/ -static int easycap_alsa_hw_free(struct snd_pcm_substream *pss) -{ - struct snd_pcm_runtime *prt; - JOT(4, "\n"); - - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - prt = pss->runtime; - if (!prt) { - SAY("ERROR: substream.runtime is NULL\n"); - return -EFAULT; - } - if (prt->dma_area) { - JOT(8, "prt->dma_area = %p\n", prt->dma_area); - vfree(prt->dma_area); - prt->dma_area = NULL; - } else - JOT(8, "dma_area already freed\n"); - return 0; -} -/*****************************************************************************/ -static int easycap_alsa_prepare(struct snd_pcm_substream *pss) -{ - struct easycap *peasycap; - struct snd_pcm_runtime *prt; - - JOT(4, "\n"); - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - prt = pss->runtime; - peasycap = snd_pcm_substream_chip(pss); - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - - JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate); - JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size); - JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods); - JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size); - JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes); - JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary); - JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step); - JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits); - JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits); - JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align); - JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base); - JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n", - pss->runtime->hw_ptr_interrupt); - - if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) { - SAY("MISTAKE: unexpected ALSA parameters\n"); - return -ENOENT; - } - return 0; -} -/*****************************************************************************/ -static int easycap_alsa_ack(struct snd_pcm_substream *pss) -{ - return 0; -} -/*****************************************************************************/ -static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd) -{ - struct easycap *peasycap; - int retval; - - JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START, - SNDRV_PCM_TRIGGER_STOP); - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - peasycap = snd_pcm_substream_chip(pss); - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: { - peasycap->audio_idle = 0; - break; - } - case SNDRV_PCM_TRIGGER_STOP: { - peasycap->audio_idle = 1; - break; - } - default: - retval = -EINVAL; - } - return 0; -} -/*****************************************************************************/ -static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss) -{ - struct easycap *peasycap; - snd_pcm_uframes_t offset; - - JOT(16, "\n"); - if (!pss) { - SAY("ERROR: pss is NULL\n"); - return -EFAULT; - } - peasycap = snd_pcm_substream_chip(pss); - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) { - JOM(8, "returning -EIO because " - "%i=audio_idle %i=audio_eof\n", - peasycap->audio_idle, peasycap->audio_eof); - return -EIO; - } -/*---------------------------------------------------------------------------*/ - if (0 > peasycap->dma_read) { - JOM(8, "returning -EBUSY\n"); - return -EBUSY; - } - offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4; - JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base); - JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n", - (int)pss->runtime->hw_ptr_interrupt); - JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n", - (int)offset, peasycap->dma_read, peasycap->dma_next); - return offset; -} -/*****************************************************************************/ -static struct page * -easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset) -{ - return vmalloc_to_page(pss->runtime->dma_area + offset); -} -/*****************************************************************************/ - -static struct snd_pcm_ops easycap_alsa_pcm_ops = { - .open = easycap_alsa_open, - .close = easycap_alsa_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = easycap_alsa_hw_params, - .hw_free = easycap_alsa_hw_free, - .prepare = easycap_alsa_prepare, - .ack = easycap_alsa_ack, - .trigger = easycap_alsa_trigger, - .pointer = easycap_alsa_pointer, - .page = easycap_alsa_page, -}; - -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS - * MEANS MODULE easycap. BEWARE. -*/ -/*---------------------------------------------------------------------------*/ -int easycap_alsa_probe(struct easycap *peasycap) -{ - int rc; - struct snd_card *psnd_card; - struct snd_pcm *psnd_pcm; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -ENODEV; - } - if (0 > peasycap->minor) { - SAY("ERROR: no minor\n"); - return -ENODEV; - } - - peasycap->alsa_hardware = alsa_hardware; - if (peasycap->microphone) { - peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000; - peasycap->alsa_hardware.rate_min = 32000; - peasycap->alsa_hardware.rate_max = 32000; - } else { - peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000; - peasycap->alsa_hardware.rate_min = 48000; - peasycap->alsa_hardware.rate_max = 48000; - } - - if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa", - THIS_MODULE, 0, &psnd_card)) { - SAY("ERROR: Cannot do ALSA snd_card_create()\n"); - return -EFAULT; - } - - sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor); - strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION); - strcpy(&psnd_card->shortname[0], "easycap_alsa"); - sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]); - - psnd_card->dev = &peasycap->pusb_device->dev; - psnd_card->private_data = peasycap; - peasycap->psnd_card = psnd_card; - - rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm); - if (rc) { - SAM("ERROR: Cannot do ALSA snd_pcm_new()\n"); - snd_card_free(psnd_card); - return -EFAULT; - } - - snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE, - &easycap_alsa_pcm_ops); - psnd_pcm->info_flags = 0; - strcpy(&psnd_pcm->name[0], &psnd_card->id[0]); - psnd_pcm->private_data = peasycap; - peasycap->psnd_pcm = psnd_pcm; - peasycap->psubstream = NULL; - - rc = snd_card_register(psnd_card); - if (rc) { - SAM("ERROR: Cannot do ALSA snd_card_register()\n"); - snd_card_free(psnd_card); - return -EFAULT; - } - - SAM("registered %s\n", &psnd_card->id[0]); - return 0; -} - -/*****************************************************************************/ -/*****************************************************************************/ -/*****************************************************************************/ -/*****************************************************************************/ -/*****************************************************************************/ -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * COMMON AUDIO INITIALIZATION - */ -/*---------------------------------------------------------------------------*/ -int -easycap_sound_setup(struct easycap *peasycap) -{ - int rc; - - JOM(4, "starting initialization\n"); - - if (!peasycap) { - SAY("ERROR: peasycap is NULL.\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -ENODEV; - } - JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device); - - rc = audio_setup(peasycap); - JOM(8, "audio_setup() returned %i\n", rc); - - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device has become NULL\n"); - return -ENODEV; - } -/*---------------------------------------------------------------------------*/ - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device has become NULL\n"); - return -ENODEV; - } - rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface, - peasycap->audio_altsetting_on); - JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface, - peasycap->audio_altsetting_on, rc); - - rc = wakeup_device(peasycap->pusb_device); - JOM(8, "wakeup_device() returned %i\n", rc); - - peasycap->audio_eof = 0; - peasycap->audio_idle = 0; - - submit_audio_urbs(peasycap); - - JOM(4, "finished initialization\n"); - return 0; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * SUBMIT ALL AUDIO URBS. - */ -/*---------------------------------------------------------------------------*/ -int -submit_audio_urbs(struct easycap *peasycap) -{ - struct data_urb *pdata_urb; - struct urb *purb; - struct list_head *plist_head; - int j, isbad, nospc, m, rc; - int isbuf; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - if (!peasycap->purb_audio_head) { - SAM("ERROR: peasycap->urb_audio_head uninitialized\n"); - return -EFAULT; - } - if (!peasycap->pusb_device) { - SAM("ERROR: peasycap->pusb_device is NULL\n"); - return -EFAULT; - } - - if (peasycap->audio_isoc_streaming) { - JOM(4, "already streaming audio urbs\n"); - return 0; - } - - JOM(4, "initial submission of all audio urbs\n"); - rc = usb_set_interface(peasycap->pusb_device, - peasycap->audio_interface, - peasycap->audio_altsetting_on); - JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", - peasycap->audio_interface, - peasycap->audio_altsetting_on, rc); - - isbad = 0; - nospc = 0; - m = 0; - list_for_each(plist_head, peasycap->purb_audio_head) { - pdata_urb = list_entry(plist_head, struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - purb = pdata_urb->purb; - isbuf = pdata_urb->isbuf; - - purb->interval = 1; - purb->dev = peasycap->pusb_device; - purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, - peasycap->audio_endpointnumber); - purb->transfer_flags = URB_ISO_ASAP; - purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo; - purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size; - purb->complete = easycap_alsa_complete; - purb->context = peasycap; - purb->start_frame = 0; - purb->number_of_packets = peasycap->audio_isoc_framesperdesc; - for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { - purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize; - purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize; - } - - rc = usb_submit_urb(purb, GFP_KERNEL); - if (rc) { - isbad++; - SAM("ERROR: usb_submit_urb() failed" - " for urb with rc: -%s: %d\n", - strerror(rc), rc); - } else { - m++; - } - } else { - isbad++; - } - } - if (nospc) { - SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc); - SAM("..... possibly inadequate USB bandwidth\n"); - peasycap->audio_eof = 1; - } - if (isbad) { - JOM(4, "attempting cleanup instead of submitting\n"); - list_for_each(plist_head, (peasycap->purb_audio_head)) { - pdata_urb = list_entry(plist_head, struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) - usb_kill_urb(pdata_urb->purb); - } - peasycap->audio_isoc_streaming = 0; - } else { - peasycap->audio_isoc_streaming = m; - JOM(4, "submitted %i audio urbs\n", m); - } - - return 0; -} -/*****************************************************************************/ -/*---------------------------------------------------------------------------*/ -/* - * KILL ALL AUDIO URBS. - */ -/*---------------------------------------------------------------------------*/ -int -kill_audio_urbs(struct easycap *peasycap) -{ - int m; - struct list_head *plist_head; - struct data_urb *pdata_urb; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return -EFAULT; - } - - if (!peasycap->audio_isoc_streaming) { - JOM(8, "%i=audio_isoc_streaming, no audio urbs killed\n", - peasycap->audio_isoc_streaming); - return 0; - } - - if (!peasycap->purb_audio_head) { - SAM("ERROR: peasycap->purb_audio_head is NULL\n"); - return -EFAULT; - } - - peasycap->audio_isoc_streaming = 0; - JOM(4, "killing audio urbs\n"); - m = 0; - list_for_each(plist_head, (peasycap->purb_audio_head)) { - pdata_urb = list_entry(plist_head, struct data_urb, list_head); - if (pdata_urb && pdata_urb->purb) { - usb_kill_urb(pdata_urb->purb); - m++; - } - } - JOM(4, "%i audio urbs killed\n", m); - - return 0; -} -/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_testcard.c b/drivers/staging/easycap/easycap_testcard.c deleted file mode 100644 index 0f71470ace39..000000000000 --- a/drivers/staging/easycap/easycap_testcard.c +++ /dev/null @@ -1,155 +0,0 @@ -/****************************************************************************** -* * -* easycap_testcard.c * -* * -******************************************************************************/ -/* - * - * Copyright (C) 2010 R.M. Thomas - * - * - * This is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * The software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this software; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * -*/ -/*****************************************************************************/ - -#include "easycap.h" - -/*****************************************************************************/ -#define TESTCARD_BYTESPERLINE (2 * 720) -void -easycap_testcard(struct easycap *peasycap, int field) -{ - int total; - int y, u, v, r, g, b; - unsigned char uyvy[4]; - int i1, line, k, m, n, more, much, barwidth, barheight; - unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2; - struct data_buffer *pfield_buffer; - - if (!peasycap) { - SAY("ERROR: peasycap is NULL\n"); - return; - } - JOM(8, "%i=field\n", field); - switch (peasycap->width) { - case 720: - case 360: { - barwidth = (2 * 720) / 8; - break; - } - case 704: - case 352: { - barwidth = (2 * 704) / 8; - break; - } - case 640: - case 320: { - barwidth = (2 * 640) / 8; - break; - } - default: { - SAM("ERROR: cannot set barwidth\n"); - return; - } - } - if (TESTCARD_BYTESPERLINE < barwidth) { - SAM("ERROR: barwidth is too large\n"); - return; - } - switch (peasycap->height) { - case 576: - case 288: { - barheight = 576; - break; - } - case 480: - case 240: { - barheight = 480; - break; - } - default: { - SAM("ERROR: cannot set barheight\n"); - return; - } - } - total = 0; - k = field; - m = 0; - n = 0; - - for (line = 0; line < (barheight / 2); line++) { - for (i1 = 0; i1 < 8; i1++) { - r = (i1 * 256)/8; - g = (i1 * 256)/8; - b = (i1 * 256)/8; - - y = 299*r/1000 + 587*g/1000 + 114*b/1000 ; - u = -147*r/1000 - 289*g/1000 + 436*b/1000 ; - u = u + 128; - v = 615*r/1000 - 515*g/1000 - 100*b/1000 ; - v = v + 128; - - uyvy[0] = 0xFF & u ; - uyvy[1] = 0xFF & y ; - uyvy[2] = 0xFF & v ; - uyvy[3] = 0xFF & y ; - - p1 = &bfbar[0]; - while (p1 < &bfbar[barwidth]) { - *p1++ = uyvy[0] ; - *p1++ = uyvy[1] ; - *p1++ = uyvy[2] ; - *p1++ = uyvy[3] ; - total += 4; - } - - p1 = &bfbar[0]; - more = barwidth; - - while (more) { - if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) { - SAM("ERROR: bad m reached\n"); - return; - } - if (PAGE_SIZE < n) { - SAM("ERROR: bad n reached\n"); - return; - } - - if (0 > more) { - SAM("ERROR: internal fault\n"); - return; - } - - much = PAGE_SIZE - n; - if (much > more) - much = more; - pfield_buffer = &peasycap->field_buffer[k][m]; - p2 = pfield_buffer->pgo + n; - memcpy(p2, p1, much); - - p1 += much; - n += much; - more -= much; - if (PAGE_SIZE == n) { - m++; - n = 0; - } - } - } - } - return; -} diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/go7007/Kconfig deleted file mode 100644 index 7dfb2815b9ec..000000000000 --- a/drivers/staging/go7007/Kconfig +++ /dev/null @@ -1,109 +0,0 @@ -config VIDEO_GO7007 - tristate "WIS GO7007 MPEG encoder support" - depends on VIDEO_DEV && PCI && I2C - depends on SND - select VIDEOBUF_DMA_SG - depends on RC_CORE - select VIDEO_TUNER - select VIDEO_TVEEPROM - select SND_PCM - select CRC32 - default N - ---help--- - This is a video4linux driver for the WIS GO7007 MPEG - encoder chip. - - To compile this driver as a module, choose M here: the - module will be called go7007 - -config VIDEO_GO7007_USB - tristate "WIS GO7007 USB support" - depends on VIDEO_GO7007 && USB - default N - ---help--- - This is a video4linux driver for the WIS GO7007 MPEG - encoder chip over USB. - - To compile this driver as a module, choose M here: the - module will be called go7007-usb - -config VIDEO_GO7007_USB_S2250_BOARD - tristate "Sensoray 2250/2251 support" - depends on VIDEO_GO7007_USB && DVB_USB - default N - ---help--- - This is a video4linux driver for the Sensoray 2250/2251 device. - - To compile this driver as a module, choose M here: the - module will be called s2250 - -config VIDEO_GO7007_OV7640 - tristate "OV7640 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the OV7640 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-ov7640 - -config VIDEO_GO7007_SAA7113 - tristate "SAA7113 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the SAA7113 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-saa7113 - -config VIDEO_GO7007_SAA7115 - tristate "SAA7115 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the SAA7115 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-saa7115 - -config VIDEO_GO7007_TW9903 - tristate "TW9903 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the TW9903 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-tw9903 - -config VIDEO_GO7007_UDA1342 - tristate "UDA1342 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the UDA1342 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-uda1342 - -config VIDEO_GO7007_SONY_TUNER - tristate "Sony tuner subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the Sony Tuner sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-sony-tuner - -config VIDEO_GO7007_TW2804 - tristate "TW2804 subdev support" - depends on VIDEO_GO7007 - default N - ---help--- - This is a video4linux driver for the TW2804 sub-device. - - To compile this driver as a module, choose M here: the - module will be called wis-tw2804 - diff --git a/drivers/staging/go7007/Makefile b/drivers/staging/go7007/Makefile deleted file mode 100644 index 6ee837c56706..000000000000 --- a/drivers/staging/go7007/Makefile +++ /dev/null @@ -1,30 +0,0 @@ -#obj-m += go7007.o go7007-usb.o snd-go7007.o wis-saa7115.o wis-tw9903.o \ - wis-uda1342.o wis-sony-tuner.o wis-saa7113.o wis-ov7640.o \ - wis-tw2804.o - - -obj-$(CONFIG_VIDEO_GO7007) += go7007.o -obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o -obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o -obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o -obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o -obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o -obj-$(CONFIG_VIDEO_GO7007_TW9903) += wis-tw9903.o -obj-$(CONFIG_VIDEO_GO7007_UDA1342) += wis-uda1342.o -obj-$(CONFIG_VIDEO_GO7007_SONY_TUNER) += wis-sony-tuner.o -obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o - -go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ - snd-go7007.o - -s2250-y := s2250-board.o - -# Uncomment when the saa7134 patches get into upstream -#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o -#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/video/saa7134 -DSAA7134_MPEG_GO7007=3 - -# S2250 needs cypress ezusb loader from dvb-usb -ccflags-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD:m=y) += -Idrivers/media/dvb/dvb-usb - -ccflags-y += -Idrivers/media/dvb/frontends -ccflags-y += -Idrivers/media/dvb/dvb-core diff --git a/drivers/staging/go7007/README b/drivers/staging/go7007/README deleted file mode 100644 index 48f447637817..000000000000 --- a/drivers/staging/go7007/README +++ /dev/null @@ -1,11 +0,0 @@ -Todo: - - checkpatch.pl cleanups - - sparse cleanups - - lots of little modules, should be merged together - and added to the build. - - testing? - - handle churn in v4l layer. - -Please send patchs to Greg Kroah-Hartman and Cc: Ross -Cohen as well. - diff --git a/drivers/staging/go7007/go7007-driver.c b/drivers/staging/go7007/go7007-driver.c deleted file mode 100644 index 6c9279a6d606..000000000000 --- a/drivers/staging/go7007/go7007-driver.c +++ /dev/null @@ -1,659 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" -#include "wis-i2c.h" - -/* - * Wait for an interrupt to be delivered from the GO7007SB and return - * the associated value and data. - * - * Must be called with the hw_lock held. - */ -int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data) -{ - go->interrupt_available = 0; - go->hpi_ops->read_interrupt(go); - if (wait_event_timeout(go->interrupt_waitq, - go->interrupt_available, 5*HZ) < 0) { - v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n"); - return -1; - } - if (!go->interrupt_available) - return -1; - go->interrupt_available = 0; - *value = go->interrupt_value & 0xfffe; - *data = go->interrupt_data; - return 0; -} -EXPORT_SYMBOL(go7007_read_interrupt); - -/* - * Read a register/address on the GO7007SB. - * - * Must be called with the hw_lock held. - */ -int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data) -{ - int count = 100; - u16 value; - - if (go7007_write_interrupt(go, 0x0010, addr) < 0) - return -EIO; - while (count-- > 0) { - if (go7007_read_interrupt(go, &value, data) == 0 && - value == 0xa000) - return 0; - } - return -EIO; -} -EXPORT_SYMBOL(go7007_read_addr); - -/* - * Send the boot firmware to the encoder, which just wakes it up and lets - * us talk to the GPIO pins and on-board I2C adapter. - * - * Must be called with the hw_lock held. - */ -static int go7007_load_encoder(struct go7007 *go) -{ - const struct firmware *fw_entry; - char fw_name[] = "go7007fw.bin"; - void *bounce; - int fw_len, rv = 0; - u16 intr_val, intr_data; - - if (request_firmware(&fw_entry, fw_name, go->dev)) { - v4l2_err(go, "unable to load firmware from file " - "\"%s\"\n", fw_name); - return -1; - } - if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { - v4l2_err(go, "file \"%s\" does not appear to be " - "go7007 firmware\n", fw_name); - release_firmware(fw_entry); - return -1; - } - fw_len = fw_entry->size - 16; - bounce = kmalloc(fw_len, GFP_KERNEL); - if (bounce == NULL) { - v4l2_err(go, "unable to allocate %d bytes for " - "firmware transfer\n", fw_len); - release_firmware(fw_entry); - return -1; - } - memcpy(bounce, fw_entry->data + 16, fw_len); - release_firmware(fw_entry); - if (go7007_interface_reset(go) < 0 || - go7007_send_firmware(go, bounce, fw_len) < 0 || - go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || - (intr_val & ~0x1) != 0x5a5a) { - v4l2_err(go, "error transferring firmware\n"); - rv = -1; - } - kfree(bounce); - return rv; -} - -MODULE_FIRMWARE("go7007fw.bin"); - -/* - * Boot the encoder and register the I2C adapter if requested. Do the - * minimum initialization necessary, since the board-specific code may - * still need to probe the board ID. - * - * Must NOT be called with the hw_lock held. - */ -int go7007_boot_encoder(struct go7007 *go, int init_i2c) -{ - int ret; - - mutex_lock(&go->hw_lock); - ret = go7007_load_encoder(go); - mutex_unlock(&go->hw_lock); - if (ret < 0) - return -1; - if (!init_i2c) - return 0; - if (go7007_i2c_init(go) < 0) - return -1; - go->i2c_adapter_online = 1; - return 0; -} -EXPORT_SYMBOL(go7007_boot_encoder); - -/* - * Configure any hardware-related registers in the GO7007, such as GPIO - * pins and bus parameters, which are board-specific. This assumes - * the boot firmware has already been downloaded. - * - * Must be called with the hw_lock held. - */ -static int go7007_init_encoder(struct go7007 *go) -{ - if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) { - go7007_write_addr(go, 0x1000, 0x0811); - go7007_write_addr(go, 0x1000, 0x0c11); - } - if (go->board_id == GO7007_BOARDID_MATRIX_REV) { - /* Set GPIO pin 0 to be an output (audio clock control) */ - go7007_write_addr(go, 0x3c82, 0x0001); - go7007_write_addr(go, 0x3c80, 0x00fe); - } - return 0; -} - -/* - * Send the boot firmware to the GO7007 and configure the registers. This - * is the only way to stop the encoder once it has started streaming video. - * - * Must be called with the hw_lock held. - */ -int go7007_reset_encoder(struct go7007 *go) -{ - if (go7007_load_encoder(go) < 0) - return -1; - return go7007_init_encoder(go); -} - -/* - * Attempt to instantiate an I2C client by ID, probably loading a module. - */ -static int init_i2c_module(struct i2c_adapter *adapter, const char *type, - int addr) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - struct v4l2_device *v4l2_dev = &go->v4l2_dev; - - if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) - return 0; - - printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); - return -1; -} - -/* - * Finalize the GO7007 hardware setup, register the on-board I2C adapter - * (if used on this board), load the I2C client driver for the sensor - * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2 - * interfaces. - * - * Must NOT be called with the hw_lock held. - */ -int go7007_register_encoder(struct go7007 *go) -{ - int i, ret; - - printk(KERN_INFO "go7007: registering new %s\n", go->name); - - mutex_lock(&go->hw_lock); - ret = go7007_init_encoder(go); - mutex_unlock(&go->hw_lock); - if (ret < 0) - return -1; - - /* v4l2 init must happen before i2c subdevs */ - ret = go7007_v4l2_init(go); - if (ret < 0) - return ret; - - if (!go->i2c_adapter_online && - go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { - if (go7007_i2c_init(go) < 0) - return -1; - go->i2c_adapter_online = 1; - } - if (go->i2c_adapter_online) { - for (i = 0; i < go->board_info->num_i2c_devs; ++i) - init_i2c_module(&go->i2c_adapter, - go->board_info->i2c_devs[i].type, - go->board_info->i2c_devs[i].addr); - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) - i2c_clients_command(&go->i2c_adapter, - DECODER_SET_CHANNEL, &go->channel_number); - } - if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) { - go->audio_enabled = 1; - go7007_snd_init(go); - } - return 0; -} -EXPORT_SYMBOL(go7007_register_encoder); - -/* - * Send the encode firmware to the encoder, which will cause it - * to immediately start delivering the video and audio streams. - * - * Must be called with the hw_lock held. - */ -int go7007_start_encoder(struct go7007 *go) -{ - u8 *fw; - int fw_len, rv = 0, i; - u16 intr_val, intr_data; - - go->modet_enable = 0; - if (!go->dvd_mode) - for (i = 0; i < 4; ++i) { - if (go->modet[i].enable) { - go->modet_enable = 1; - continue; - } - go->modet[i].pixel_threshold = 32767; - go->modet[i].motion_threshold = 32767; - go->modet[i].mb_threshold = 32767; - } - - if (go7007_construct_fw_image(go, &fw, &fw_len) < 0) - return -1; - - if (go7007_send_firmware(go, fw, fw_len) < 0 || - go7007_read_interrupt(go, &intr_val, &intr_data) < 0) { - v4l2_err(&go->v4l2_dev, "error transferring firmware\n"); - rv = -1; - goto start_error; - } - - go->state = STATE_DATA; - go->parse_length = 0; - go->seen_frame = 0; - if (go7007_stream_start(go) < 0) { - v4l2_err(&go->v4l2_dev, "error starting stream transfer\n"); - rv = -1; - goto start_error; - } - -start_error: - kfree(fw); - return rv; -} - -/* - * Store a byte in the current video buffer, if there is one. - */ -static inline void store_byte(struct go7007_buffer *gobuf, u8 byte) -{ - if (gobuf != NULL && gobuf->bytesused < GO7007_BUF_SIZE) { - unsigned int pgidx = gobuf->offset >> PAGE_SHIFT; - unsigned int pgoff = gobuf->offset & ~PAGE_MASK; - - *((u8 *)page_address(gobuf->pages[pgidx]) + pgoff) = byte; - ++gobuf->offset; - ++gobuf->bytesused; - } -} - -/* - * Deliver the last video buffer and get a new one to start writing to. - */ -static void frame_boundary(struct go7007 *go) -{ - struct go7007_buffer *gobuf; - int i; - - if (go->active_buf) { - if (go->active_buf->modet_active) { - if (go->active_buf->bytesused + 216 < GO7007_BUF_SIZE) { - for (i = 0; i < 216; ++i) - store_byte(go->active_buf, - go->active_map[i]); - go->active_buf->bytesused -= 216; - } else - go->active_buf->modet_active = 0; - } - go->active_buf->state = BUF_STATE_DONE; - wake_up_interruptible(&go->frame_waitq); - go->active_buf = NULL; - } - list_for_each_entry(gobuf, &go->stream, stream) - if (gobuf->state == BUF_STATE_QUEUED) { - gobuf->seq = go->next_seq; - do_gettimeofday(&gobuf->timestamp); - go->active_buf = gobuf; - break; - } - ++go->next_seq; -} - -static void write_bitmap_word(struct go7007 *go) -{ - int x, y, i, stride = ((go->width >> 4) + 7) >> 3; - - for (i = 0; i < 16; ++i) { - y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4); - x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4); - if (stride * y + (x >> 3) < sizeof(go->active_map)) - go->active_map[stride * y + (x >> 3)] |= - (go->modet_word & 1) << (x & 0x7); - go->modet_word >>= 1; - } -} - -/* - * Parse a chunk of the video stream into frames. The frames are not - * delimited by the hardware, so we have to parse the frame boundaries - * based on the type of video stream we're receiving. - */ -void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) -{ - int i, seq_start_code = -1, frame_start_code = -1; - - spin_lock(&go->spinlock); - - switch (go->format) { - case GO7007_FORMAT_MPEG4: - seq_start_code = 0xB0; - frame_start_code = 0xB6; - break; - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: - seq_start_code = 0xB3; - frame_start_code = 0x00; - break; - } - - for (i = 0; i < length; ++i) { - if (go->active_buf != NULL && - go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) { - v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); - go->active_buf->offset -= go->active_buf->bytesused; - go->active_buf->bytesused = 0; - go->active_buf->modet_active = 0; - go->active_buf = NULL; - } - - switch (go->state) { - case STATE_DATA: - switch (buf[i]) { - case 0x00: - go->state = STATE_00; - break; - case 0xFF: - go->state = STATE_FF; - break; - default: - store_byte(go->active_buf, buf[i]); - break; - } - break; - case STATE_00: - switch (buf[i]) { - case 0x00: - go->state = STATE_00_00; - break; - case 0xFF: - store_byte(go->active_buf, 0x00); - go->state = STATE_FF; - break; - default: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_00_00: - switch (buf[i]) { - case 0x00: - store_byte(go->active_buf, 0x00); - /* go->state remains STATE_00_00 */ - break; - case 0x01: - go->state = STATE_00_00_01; - break; - case 0xFF: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - go->state = STATE_FF; - break; - default: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_00_00_01: - if (buf[i] == 0xF8 && go->modet_enable == 0) { - /* MODET start code, but MODET not enabled */ - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x01); - store_byte(go->active_buf, 0xF8); - go->state = STATE_DATA; - break; - } - /* If this is the start of a new MPEG frame, - * get a new buffer */ - if ((go->format == GO7007_FORMAT_MPEG1 || - go->format == GO7007_FORMAT_MPEG2 || - go->format == GO7007_FORMAT_MPEG4) && - (buf[i] == seq_start_code || - buf[i] == 0xB8 || /* GOP code */ - buf[i] == frame_start_code)) { - if (go->active_buf == NULL || go->seen_frame) - frame_boundary(go); - if (buf[i] == frame_start_code) { - if (go->active_buf != NULL) - go->active_buf->frame_offset = - go->active_buf->offset; - go->seen_frame = 1; - } else { - go->seen_frame = 0; - } - } - /* Handle any special chunk types, or just write the - * start code to the (potentially new) buffer */ - switch (buf[i]) { - case 0xF5: /* timestamp */ - go->parse_length = 12; - go->state = STATE_UNPARSED; - break; - case 0xF6: /* vbi */ - go->state = STATE_VBI_LEN_A; - break; - case 0xF8: /* MD map */ - go->parse_length = 0; - memset(go->active_map, 0, - sizeof(go->active_map)); - go->state = STATE_MODET_MAP; - break; - case 0xFF: /* Potential JPEG start code */ - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x01); - go->state = STATE_FF; - break; - default: - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x00); - store_byte(go->active_buf, 0x01); - store_byte(go->active_buf, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_FF: - switch (buf[i]) { - case 0x00: - store_byte(go->active_buf, 0xFF); - go->state = STATE_00; - break; - case 0xFF: - store_byte(go->active_buf, 0xFF); - /* go->state remains STATE_FF */ - break; - case 0xD8: - if (go->format == GO7007_FORMAT_MJPEG) - frame_boundary(go); - /* fall through */ - default: - store_byte(go->active_buf, 0xFF); - store_byte(go->active_buf, buf[i]); - go->state = STATE_DATA; - break; - } - break; - case STATE_VBI_LEN_A: - go->parse_length = buf[i] << 8; - go->state = STATE_VBI_LEN_B; - break; - case STATE_VBI_LEN_B: - go->parse_length |= buf[i]; - if (go->parse_length > 0) - go->state = STATE_UNPARSED; - else - go->state = STATE_DATA; - break; - case STATE_MODET_MAP: - if (go->parse_length < 204) { - if (go->parse_length & 1) { - go->modet_word |= buf[i]; - write_bitmap_word(go); - } else - go->modet_word = buf[i] << 8; - } else if (go->parse_length == 207 && go->active_buf) { - go->active_buf->modet_active = buf[i]; - } - if (++go->parse_length == 208) - go->state = STATE_DATA; - break; - case STATE_UNPARSED: - if (--go->parse_length == 0) - go->state = STATE_DATA; - break; - } - } - - spin_unlock(&go->spinlock); -} -EXPORT_SYMBOL(go7007_parse_video_stream); - -/* - * Allocate a new go7007 struct. Used by the hardware-specific probe. - */ -struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) -{ - struct go7007 *go; - int i; - - go = kmalloc(sizeof(struct go7007), GFP_KERNEL); - if (go == NULL) - return NULL; - go->dev = dev; - go->board_info = board; - go->board_id = 0; - go->tuner_type = -1; - go->channel_number = 0; - go->name[0] = 0; - mutex_init(&go->hw_lock); - init_waitqueue_head(&go->frame_waitq); - spin_lock_init(&go->spinlock); - go->video_dev = NULL; - go->ref_count = 0; - go->status = STATUS_INIT; - memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter)); - go->i2c_adapter_online = 0; - go->interrupt_available = 0; - init_waitqueue_head(&go->interrupt_waitq); - go->in_use = 0; - go->input = 0; - if (board->sensor_flags & GO7007_SENSOR_TV) { - go->standard = GO7007_STD_NTSC; - go->width = 720; - go->height = 480; - go->sensor_framerate = 30000; - } else { - go->standard = GO7007_STD_OTHER; - go->width = board->sensor_width; - go->height = board->sensor_height; - go->sensor_framerate = board->sensor_framerate; - } - go->encoder_v_offset = board->sensor_v_offset; - go->encoder_h_offset = board->sensor_h_offset; - go->encoder_h_halve = 0; - go->encoder_v_halve = 0; - go->encoder_subsample = 0; - go->streaming = 0; - go->format = GO7007_FORMAT_MJPEG; - go->bitrate = 1500000; - go->fps_scale = 1; - go->pali = 0; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = 0; - go->ipb = 0; - go->closed_gop = 0; - go->repeat_seqhead = 0; - go->seq_header_enable = 0; - go->gop_header_enable = 0; - go->dvd_mode = 0; - go->interlace_coding = 0; - for (i = 0; i < 4; ++i) - go->modet[i].enable = 0; - for (i = 0; i < 1624; ++i) - go->modet_map[i] = 0; - go->audio_deliver = NULL; - go->audio_enabled = 0; - INIT_LIST_HEAD(&go->stream); - - return go; -} -EXPORT_SYMBOL(go7007_alloc); - -/* - * Detach and unregister the encoder. The go7007 struct won't be freed - * until v4l2 finishes releasing its resources and all associated fds are - * closed by applications. - */ -void go7007_remove(struct go7007 *go) -{ - if (go->i2c_adapter_online) { - if (i2c_del_adapter(&go->i2c_adapter) == 0) - go->i2c_adapter_online = 0; - else - v4l2_err(&go->v4l2_dev, - "error removing I2C adapter!\n"); - } - - if (go->audio_enabled) - go7007_snd_remove(go); - go7007_v4l2_remove(go); -} -EXPORT_SYMBOL(go7007_remove); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/go7007-fw.c b/drivers/staging/go7007/go7007-fw.c deleted file mode 100644 index c9a6409edfe3..000000000000 --- a/drivers/staging/go7007/go7007-fw.c +++ /dev/null @@ -1,1636 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -/* - * This file contains code to generate a firmware image for the GO7007SB - * encoder. Much of the firmware is read verbatim from a file, but some of - * it concerning bitrate control and other things that can be configured at - * run-time are generated dynamically. Note that the format headers - * generated here do not affect the functioning of the encoder; they are - * merely parroted back to the host at the start of each frame. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -/* Constants used in the source firmware image to describe code segments */ - -#define FLAG_MODE_MJPEG (1) -#define FLAG_MODE_MPEG1 (1<<1) -#define FLAG_MODE_MPEG2 (1<<2) -#define FLAG_MODE_MPEG4 (1<<3) -#define FLAG_MODE_H263 (1<<4) -#define FLAG_MODE_ALL (FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \ - FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \ - FLAG_MODE_H263) -#define FLAG_SPECIAL (1<<8) - -#define SPECIAL_FRM_HEAD 0 -#define SPECIAL_BRC_CTRL 1 -#define SPECIAL_CONFIG 2 -#define SPECIAL_SEQHEAD 3 -#define SPECIAL_AV_SYNC 4 -#define SPECIAL_FINAL 5 -#define SPECIAL_AUDIO 6 -#define SPECIAL_MODET 7 - -/* Little data class for creating MPEG headers bit-by-bit */ - -struct code_gen { - unsigned char *p; /* destination */ - u32 a; /* collects bits at the top of the variable */ - int b; /* bit position of most recently-written bit */ - int len; /* written out so far */ -}; - -#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 } - -#define CODE_ADD(name, val, length) do { \ - name.b -= (length); \ - name.a |= (val) << name.b; \ - while (name.b <= 24) { \ - *name.p = name.a >> 24; \ - ++name.p; \ - name.a <<= 8; \ - name.b += 8; \ - name.len += 8; \ - } \ -} while (0) - -#define CODE_LENGTH(name) (name.len + (32 - name.b)) - -/* Tables for creating the bitrate control data */ - -static const s16 converge_speed_ip[101] = { - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, - 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, - 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, - 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, - 19, 20, 22, 23, 25, 27, 30, 32, 35, 38, - 41, 45, 49, 53, 58, 63, 69, 76, 83, 91, - 100 -}; - -static const s16 converge_speed_ipb[101] = { - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, - 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, - 9, 9, 10, 10, 11, 12, 12, 13, 14, 14, - 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, - 28, 30, 32, 34, 37, 40, 42, 46, 49, 53, - 57, 61, 66, 71, 77, 83, 90, 97, 106, 115, - 125, 135, 147, 161, 175, 191, 209, 228, 249, 273, - 300 -}; - -static const s16 LAMBDA_table[4][101] = { - { 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, - 19, 19, 19, 20, 20, 20, 21, 21, 22, 22, - 22, 23, 23, 24, 24, 25, 25, 25, 26, 26, - 27, 27, 28, 28, 29, 29, 30, 31, 31, 32, - 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, - 39, 39, 40, 41, 42, 42, 43, 44, 45, 46, - 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, - 67, 68, 69, 70, 72, 73, 74, 76, 77, 78, - 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, - 96 - }, - { - 20, 20, 20, 21, 21, 21, 22, 22, 23, 23, - 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, - 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, - 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, - 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, - 70, 71, 72, 73, 75, 76, 78, 79, 80, 82, - 83, 85, 86, 88, 90, 91, 93, 95, 96, 98, - 100, 102, 103, 105, 107, 109, 111, 113, 115, 117, - 120 - }, - { - 24, 24, 24, 25, 25, 26, 26, 27, 27, 28, - 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, - 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, - 41, 41, 42, 43, 44, 44, 45, 46, 47, 48, - 49, 50, 50, 51, 52, 53, 54, 55, 56, 57, - 58, 59, 60, 62, 63, 64, 65, 66, 67, 69, - 70, 71, 72, 74, 75, 76, 78, 79, 81, 82, - 84, 85, 87, 88, 90, 92, 93, 95, 97, 98, - 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, - 120, 122, 124, 127, 129, 131, 134, 136, 138, 141, - 144 - }, - { - 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, - 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, - 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 62, 63, 64, - 65, 66, 67, 69, 70, 71, 72, 74, 75, 76, - 78, 79, 81, 82, 84, 85, 87, 88, 90, 92, - 93, 95, 97, 98, 100, 102, 104, 106, 108, 110, - 112, 114, 116, 118, 120, 122, 124, 127, 129, 131, - 134, 136, 139, 141, 144, 146, 149, 152, 154, 157, - 160, 163, 166, 169, 172, 175, 178, 181, 185, 188, - 192 - } -}; - -/* MPEG blank frame generation tables */ - -enum mpeg_frame_type { - PFRAME, - BFRAME_PRE, - BFRAME_POST, - BFRAME_BIDIR, - BFRAME_EMPTY -}; - -static const u32 addrinctab[33][2] = { - { 0x01, 1 }, { 0x03, 3 }, { 0x02, 3 }, { 0x03, 4 }, - { 0x02, 4 }, { 0x03, 5 }, { 0x02, 5 }, { 0x07, 7 }, - { 0x06, 7 }, { 0x0b, 8 }, { 0x0a, 8 }, { 0x09, 8 }, - { 0x08, 8 }, { 0x07, 8 }, { 0x06, 8 }, { 0x17, 10 }, - { 0x16, 10 }, { 0x15, 10 }, { 0x14, 10 }, { 0x13, 10 }, - { 0x12, 10 }, { 0x23, 11 }, { 0x22, 11 }, { 0x21, 11 }, - { 0x20, 11 }, { 0x1f, 11 }, { 0x1e, 11 }, { 0x1d, 11 }, - { 0x1c, 11 }, { 0x1b, 11 }, { 0x1a, 11 }, { 0x19, 11 }, - { 0x18, 11 } -}; - -/* Standard JPEG tables */ - -static const u8 default_intra_quant_table[] = { - 8, 16, 19, 22, 26, 27, 29, 34, - 16, 16, 22, 24, 27, 29, 34, 37, - 19, 22, 26, 27, 29, 34, 34, 38, - 22, 22, 26, 27, 29, 34, 37, 40, - 22, 26, 27, 29, 32, 35, 40, 48, - 26, 27, 29, 32, 35, 40, 48, 58, - 26, 27, 29, 34, 38, 46, 56, 69, - 27, 29, 35, 38, 46, 56, 69, 83 -}; - -static const u8 bits_dc_luminance[] = { - 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 -}; - -static const u8 val_dc_luminance[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 -}; - -static const u8 bits_dc_chrominance[] = { - 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 -}; - -static const u8 val_dc_chrominance[] = { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 -}; - -static const u8 bits_ac_luminance[] = { - 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d -}; - -static const u8 val_ac_luminance[] = { - 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 -}; - -static const u8 bits_ac_chrominance[] = { - 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 -}; - -static const u8 val_ac_chrominance[] = { - 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 -}; - -/* Zig-zag mapping for quant table - * - * OK, let's do this mapping on the actual table above so it doesn't have - * to be done on the fly. - */ -static const int zz[64] = { - 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 -}; - -static int copy_packages(__le16 *dest, u16 *src, int pkg_cnt, int space) -{ - int i, cnt = pkg_cnt * 32; - - if (space < cnt) - return -1; - - for (i = 0; i < cnt; ++i) - dest[i] = cpu_to_le16p(src + i); - - return cnt; -} - -static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q) -{ - int i, p = 0; - - buf[p++] = 0xff; - buf[p++] = 0xd8; - buf[p++] = 0xff; - buf[p++] = 0xdb; - buf[p++] = 0; - buf[p++] = 2 + 65; - buf[p++] = 0; - buf[p++] = default_intra_quant_table[0]; - for (i = 1; i < 64; ++i) - /* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */ - buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3; - buf[p++] = 0xff; - buf[p++] = 0xc0; - buf[p++] = 0; - buf[p++] = 17; - buf[p++] = 8; - buf[p++] = go->height >> 8; - buf[p++] = go->height & 0xff; - buf[p++] = go->width >> 8; - buf[p++] = go->width & 0xff; - buf[p++] = 3; - buf[p++] = 1; - buf[p++] = 0x22; - buf[p++] = 0; - buf[p++] = 2; - buf[p++] = 0x11; - buf[p++] = 0; - buf[p++] = 3; - buf[p++] = 0x11; - buf[p++] = 0; - buf[p++] = 0xff; - buf[p++] = 0xc4; - buf[p++] = 418 >> 8; - buf[p++] = 418 & 0xff; - buf[p++] = 0x00; - memcpy(buf + p, bits_dc_luminance + 1, 16); - p += 16; - memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance)); - p += sizeof(val_dc_luminance); - buf[p++] = 0x01; - memcpy(buf + p, bits_dc_chrominance + 1, 16); - p += 16; - memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance)); - p += sizeof(val_dc_chrominance); - buf[p++] = 0x10; - memcpy(buf + p, bits_ac_luminance + 1, 16); - p += 16; - memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance)); - p += sizeof(val_ac_luminance); - buf[p++] = 0x11; - memcpy(buf + p, bits_ac_chrominance + 1, 16); - p += 16; - memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance)); - p += sizeof(val_ac_chrominance); - buf[p++] = 0xff; - buf[p++] = 0xda; - buf[p++] = 0; - buf[p++] = 12; - buf[p++] = 3; - buf[p++] = 1; - buf[p++] = 0x00; - buf[p++] = 2; - buf[p++] = 0x11; - buf[p++] = 3; - buf[p++] = 0x11; - buf[p++] = 0; - buf[p++] = 63; - buf[p++] = 0; - return p; -} - -static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) -{ - u8 *buf; - u16 mem = 0x3e00; - unsigned int addr = 0x19; - int size = 0, i, off = 0, chunk; - - buf = kzalloc(4096, GFP_KERNEL); - if (buf == NULL) { - printk(KERN_ERR "go7007: unable to allocate 4096 bytes for " - "firmware construction\n"); - return -1; - } - - for (i = 1; i < 32; ++i) { - mjpeg_frame_header(go, buf + size, i); - size += 80; - } - chunk = mjpeg_frame_header(go, buf + size, 1); - memmove(buf + size, buf + size + 80, chunk - 80); - size += chunk - 80; - - for (i = 0; i < size; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > size) - chunk = (size - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr++); - mem = 0x3e00; - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } -done: - kfree(buf); - return off; -} - -static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf, - int modulo, int pict_struct, enum mpeg_frame_type frame) -{ - int i, j, mb_code, mb_len; - int rows = go->interlace_coding ? go->height / 32 : go->height / 16; - CODE_GEN(c, buf + 6); - - switch (frame) { - case PFRAME: - mb_code = 0x1; - mb_len = 3; - break; - case BFRAME_PRE: - mb_code = 0x2; - mb_len = 4; - break; - case BFRAME_POST: - mb_code = 0x2; - mb_len = 3; - break; - case BFRAME_BIDIR: - mb_code = 0x2; - mb_len = 2; - break; - default: /* keep the compiler happy */ - mb_code = mb_len = 0; - break; - } - - CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13); - CODE_ADD(c, 0xffff, 16); - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4); - if (frame != PFRAME) - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4); - else - CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */ - CODE_ADD(c, 0, 3); /* What is this?? */ - /* Byte-align with zeros */ - j = 8 - (CODE_LENGTH(c) % 8); - if (j != 8) - CODE_ADD(c, 0, j); - - if (go->format == GO7007_FORMAT_MPEG2) { - CODE_ADD(c, 0x1, 24); - CODE_ADD(c, 0xb5, 8); - CODE_ADD(c, 0x844, 12); - CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8); - if (go->interlace_coding) { - CODE_ADD(c, pict_struct, 4); - if (go->dvd_mode) - CODE_ADD(c, 0x000, 11); - else - CODE_ADD(c, 0x200, 11); - } else { - CODE_ADD(c, 0x3, 4); - CODE_ADD(c, 0x20c, 11); - } - /* Byte-align with zeros */ - j = 8 - (CODE_LENGTH(c) % 8); - if (j != 8) - CODE_ADD(c, 0, j); - } - - for (i = 0; i < rows; ++i) { - CODE_ADD(c, 1, 24); - CODE_ADD(c, i + 1, 8); - CODE_ADD(c, 0x2, 6); - CODE_ADD(c, 0x1, 1); - CODE_ADD(c, mb_code, mb_len); - if (go->interlace_coding) { - CODE_ADD(c, 0x1, 2); - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - if (frame == BFRAME_BIDIR) { - CODE_ADD(c, 0x3, 2); - if (go->interlace_coding) - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - CODE_ADD(c, 0x3, 2); - for (j = (go->width >> 4) - 2; j >= 33; j -= 33) - CODE_ADD(c, 0x8, 11); - CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]); - CODE_ADD(c, mb_code, mb_len); - if (go->interlace_coding) { - CODE_ADD(c, 0x1, 2); - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - if (frame == BFRAME_BIDIR) { - CODE_ADD(c, 0x3, 2); - if (go->interlace_coding) - CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); - } - CODE_ADD(c, 0x3, 2); - - /* Byte-align with zeros */ - j = 8 - (CODE_LENGTH(c) % 8); - if (j != 8) - CODE_ADD(c, 0, j); - } - - i = CODE_LENGTH(c) + 4 * 8; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x01; - buf[5] = 0x00; - return i; -} - -static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) -{ - int i, aspect_ratio, picture_rate; - CODE_GEN(c, buf + 6); - - if (go->format == GO7007_FORMAT_MPEG1) { - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; - break; - case GO7007_RATIO_16_9: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; - break; - default: - aspect_ratio = 1; - break; - } - } else { - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - aspect_ratio = 2; - break; - case GO7007_RATIO_16_9: - aspect_ratio = 3; - break; - default: - aspect_ratio = 1; - break; - } - } - switch (go->sensor_framerate) { - case 24000: - picture_rate = 1; - break; - case 24024: - picture_rate = 2; - break; - case 25025: - picture_rate = go->interlace_coding ? 6 : 3; - break; - case 30000: - picture_rate = go->interlace_coding ? 7 : 4; - break; - case 30030: - picture_rate = go->interlace_coding ? 8 : 5; - break; - default: - picture_rate = 5; /* 30 fps seems like a reasonable default */ - break; - } - - CODE_ADD(c, go->width, 12); - CODE_ADD(c, go->height, 12); - CODE_ADD(c, aspect_ratio, 4); - CODE_ADD(c, picture_rate, 4); - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 20000 : 0x3ffff, 18); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 112 : 20, 10); - CODE_ADD(c, 0, 3); - - /* Byte-align with zeros */ - i = 8 - (CODE_LENGTH(c) % 8); - if (i != 8) - CODE_ADD(c, 0, i); - - if (go->format == GO7007_FORMAT_MPEG2) { - CODE_ADD(c, 0x1, 24); - CODE_ADD(c, 0xb5, 8); - CODE_ADD(c, 0x148, 12); - if (go->interlace_coding) - CODE_ADD(c, 0x20001, 20); - else - CODE_ADD(c, 0xa0001, 20); - CODE_ADD(c, 0, 16); - - /* Byte-align with zeros */ - i = 8 - (CODE_LENGTH(c) % 8); - if (i != 8) - CODE_ADD(c, 0, i); - - if (ext) { - CODE_ADD(c, 0x1, 24); - CODE_ADD(c, 0xb52, 12); - CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3); - CODE_ADD(c, 0x105, 9); - CODE_ADD(c, 0x505, 16); - CODE_ADD(c, go->width, 14); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->height, 14); - - /* Byte-align with zeros */ - i = 8 - (CODE_LENGTH(c) % 8); - if (i != 8) - CODE_ADD(c, 0, i); - } - } - - i = CODE_LENGTH(c) + 4 * 8; - buf[0] = i & 0xff; - buf[1] = i >> 8; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x01; - buf[5] = 0xb3; - return i; -} - -static int gen_mpeg1hdr_to_package(struct go7007 *go, - __le16 *code, int space, int *framelen) -{ - u8 *buf; - u16 mem = 0x3e00; - unsigned int addr = 0x19; - int i, off = 0, chunk; - - buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) { - printk(KERN_ERR "go7007: unable to allocate 5120 bytes for " - "firmware construction\n"); - return -1; - } - framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME); - if (go->interlace_coding) - framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8, - 0, 2, PFRAME); - buf[0] = framelen[0] & 0xff; - buf[1] = framelen[0] >> 8; - i = 368; - framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE); - if (go->interlace_coding) - framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8, - 0, 2, BFRAME_PRE); - buf[i] = framelen[1] & 0xff; - buf[i + 1] = framelen[1] >> 8; - i += 1632; - framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST); - if (go->interlace_coding) - framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8, - 0, 2, BFRAME_POST); - buf[i] = framelen[2] & 0xff; - buf[i + 1] = framelen[2] >> 8; - i += 1432; - framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR); - if (go->interlace_coding) - framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8, - 0, 2, BFRAME_BIDIR); - buf[i] = framelen[3] & 0xff; - buf[i + 1] = framelen[3] >> 8; - i += 1632 + 16; - mpeg1_sequence_header(go, buf + i, 0); - i += 40; - for (i = 0; i < 5120; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > 5120) - chunk = (5120 - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr); - if (mem + chunk == 0x4000) { - mem = 0x3e00; - ++addr; - } - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } -done: - kfree(buf); - return off; -} - -static int vti_bitlen(struct go7007 *go) -{ - unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale; - - for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i); - return i + 1; -} - -static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf, - int modulo, enum mpeg_frame_type frame) -{ - int i; - CODE_GEN(c, buf + 6); - int mb_count = (go->width >> 4) * (go->height >> 4); - - CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2); - if (modulo) - CODE_ADD(c, 0x1, 1); - CODE_ADD(c, 0x1, 2); - CODE_ADD(c, 0, vti_bitlen(go)); - CODE_ADD(c, 0x3, 2); - if (frame == PFRAME) - CODE_ADD(c, 0, 1); - CODE_ADD(c, 0xc, 11); - if (frame != PFRAME) - CODE_ADD(c, 0x4, 3); - if (frame != BFRAME_EMPTY) { - for (i = 0; i < mb_count; ++i) { - switch (frame) { - case PFRAME: - CODE_ADD(c, 0x1, 1); - break; - case BFRAME_PRE: - CODE_ADD(c, 0x47, 8); - break; - case BFRAME_POST: - CODE_ADD(c, 0x27, 7); - break; - case BFRAME_BIDIR: - CODE_ADD(c, 0x5f, 8); - break; - case BFRAME_EMPTY: /* keep compiler quiet */ - break; - } - } - } - - /* Byte-align with a zero followed by ones */ - i = 8 - (CODE_LENGTH(c) % 8); - CODE_ADD(c, 0, 1); - CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); - - i = CODE_LENGTH(c) + 4 * 8; - buf[0] = i & 0xff; - buf[1] = i >> 8; - buf[2] = 0x00; - buf[3] = 0x00; - buf[4] = 0x01; - buf[5] = 0xb6; - return i; -} - -static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext) -{ - const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali, - 0x00, 0x00, 0x01, 0xb5, 0x09, - 0x00, 0x00, 0x01, 0x00, - 0x00, 0x00, 0x01, 0x20, }; - int i, aspect_ratio; - int fps = go->sensor_framerate / go->fps_scale; - CODE_GEN(c, buf + 2 + sizeof(head)); - - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; - break; - case GO7007_RATIO_16_9: - aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; - break; - default: - aspect_ratio = 1; - break; - } - - memcpy(buf + 2, head, sizeof(head)); - CODE_ADD(c, 0x191, 17); - CODE_ADD(c, aspect_ratio, 4); - CODE_ADD(c, 0x1, 4); - CODE_ADD(c, fps, 16); - CODE_ADD(c, 0x3, 2); - CODE_ADD(c, 1001, vti_bitlen(go)); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->width, 13); - CODE_ADD(c, 1, 1); - CODE_ADD(c, go->height, 13); - CODE_ADD(c, 0x2830, 14); - - /* Byte-align */ - i = 8 - (CODE_LENGTH(c) % 8); - CODE_ADD(c, 0, 1); - CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); - - i = CODE_LENGTH(c) + sizeof(head) * 8; - buf[0] = i & 0xff; - buf[1] = i >> 8; - return i; -} - -static int gen_mpeg4hdr_to_package(struct go7007 *go, - __le16 *code, int space, int *framelen) -{ - u8 *buf; - u16 mem = 0x3e00; - unsigned int addr = 0x19; - int i, off = 0, chunk; - - buf = kzalloc(5120, GFP_KERNEL); - if (buf == NULL) { - printk(KERN_ERR "go7007: unable to allocate 5120 bytes for " - "firmware construction\n"); - return -1; - } - framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME); - i = 368; - framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE); - i += 1632; - framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST); - i += 1432; - framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR); - i += 1632; - mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY); - i += 16; - mpeg4_sequence_header(go, buf + i, 0); - i += 40; - for (i = 0; i < 5120; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > 5120) - chunk = (5120 - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr); - if (mem + chunk == 0x4000) { - mem = 0x3e00; - ++addr; - } - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } - mem = 0x3e00; - addr = go->ipb ? 0x14f9 : 0x0af9; - memset(buf, 0, 5120); - framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME); - i = 368; - framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE); - i += 1632; - framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST); - i += 1432; - framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR); - i += 1632; - mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY); - i += 16; - for (i = 0; i < 5120; i += chunk * 2) { - if (space - off < 32) { - off = -1; - goto done; - } - - code[off + 1] = __cpu_to_le16(0x8000 | mem); - - chunk = 28; - if (mem + chunk > 0x4000) - chunk = 0x4000 - mem; - if (i + 2 * chunk > 5120) - chunk = (5120 - i) / 2; - - if (chunk < 28) { - code[off] = __cpu_to_le16(0x4000 | chunk); - code[off + 31] = __cpu_to_le16(addr); - if (mem + chunk == 0x4000) { - mem = 0x3e00; - ++addr; - } - } else { - code[off] = __cpu_to_le16(0x1000 | 28); - code[off + 31] = 0; - mem += 28; - } - - memcpy(&code[off + 2], buf + i, chunk * 2); - off += 32; - } -done: - kfree(buf); - return off; -} - -static int brctrl_to_package(struct go7007 *go, - __le16 *code, int space, int *framelen) -{ - int converge_speed = 0; - int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ? - 100 : 0; - int peak_rate = 6 * go->bitrate / 5; - int vbv_buffer = go->format == GO7007_FORMAT_MJPEG ? - go->bitrate : - (go->dvd_mode ? 900000 : peak_rate); - int fps = go->sensor_framerate / go->fps_scale; - int q = 0; - /* Bizarre math below depends on rounding errors in division */ - u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps; - u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps; - u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000); - u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32); - u32 cplx[] = { - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - q > 0 ? sgop_expt_addr * q : - 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, - }; - u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr; - u16 pack[] = { - 0x200e, 0x0000, - 0xBF20, go->ipb ? converge_speed_ipb[converge_speed] - : converge_speed_ip[converge_speed], - 0xBF21, go->ipb ? 2 : 0, - 0xBF22, go->ipb ? LAMBDA_table[0][lambda / 2 + 50] - : 32767, - 0xBF23, go->ipb ? LAMBDA_table[1][lambda] : 32767, - 0xBF24, 32767, - 0xBF25, lambda > 99 ? 32767 : LAMBDA_table[3][lambda], - 0xBF26, sgop_expt_addr & 0x0000FFFF, - 0xBF27, sgop_expt_addr >> 16, - 0xBF28, sgop_peak_addr & 0x0000FFFF, - 0xBF29, sgop_peak_addr >> 16, - 0xBF2A, vbv_alert_addr & 0x0000FFFF, - 0xBF2B, vbv_alert_addr >> 16, - 0xBF2C, 0, - 0xBF2D, 0, - 0, 0, - - 0x200e, 0x0000, - 0xBF2E, vbv_alert_addr & 0x0000FFFF, - 0xBF2F, vbv_alert_addr >> 16, - 0xBF30, cplx[0] & 0x0000FFFF, - 0xBF31, cplx[0] >> 16, - 0xBF32, cplx[1] & 0x0000FFFF, - 0xBF33, cplx[1] >> 16, - 0xBF34, cplx[2] & 0x0000FFFF, - 0xBF35, cplx[2] >> 16, - 0xBF36, cplx[3] & 0x0000FFFF, - 0xBF37, cplx[3] >> 16, - 0xBF38, 0, - 0xBF39, 0, - 0xBF3A, total_expt_addr & 0x0000FFFF, - 0xBF3B, total_expt_addr >> 16, - 0, 0, - - 0x200e, 0x0000, - 0xBF3C, total_expt_addr & 0x0000FFFF, - 0xBF3D, total_expt_addr >> 16, - 0xBF3E, 0, - 0xBF3F, 0, - 0xBF48, 0, - 0xBF49, 0, - 0xBF4A, calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q), - 0xBF4B, 4, - 0xBF4C, 0, - 0xBF4D, 0, - 0xBF4E, 0, - 0xBF4F, 0, - 0xBF50, 0, - 0xBF51, 0, - 0, 0, - - 0x200e, 0x0000, - 0xBF40, sgop_expt_addr & 0x0000FFFF, - 0xBF41, sgop_expt_addr >> 16, - 0xBF42, 0, - 0xBF43, 0, - 0xBF44, 0, - 0xBF45, 0, - 0xBF46, (go->width >> 4) * (go->height >> 4), - 0xBF47, 0, - 0xBF64, 0, - 0xBF65, 0, - 0xBF18, framelen[4], - 0xBF19, framelen[5], - 0xBF1A, framelen[6], - 0xBF1B, framelen[7], - 0, 0, - -#if 0 - /* Remove once we don't care about matching */ - 0x200e, 0x0000, - 0xBF56, 4, - 0xBF57, 0, - 0xBF58, 5, - 0xBF59, 0, - 0xBF5A, 6, - 0xBF5B, 0, - 0xBF5C, 8, - 0xBF5D, 0, - 0xBF5E, 1, - 0xBF5F, 0, - 0xBF60, 1, - 0xBF61, 0, - 0xBF62, 0, - 0xBF63, 0, - 0, 0, -#else - 0x2008, 0x0000, - 0xBF56, 4, - 0xBF57, 0, - 0xBF58, 5, - 0xBF59, 0, - 0xBF5A, 6, - 0xBF5B, 0, - 0xBF5C, 8, - 0xBF5D, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, -#endif - - 0x200e, 0x0000, - 0xBF10, 0, - 0xBF11, 0, - 0xBF12, 0, - 0xBF13, 0, - 0xBF14, 0, - 0xBF15, 0, - 0xBF16, 0, - 0xBF17, 0, - 0xBF7E, 0, - 0xBF7F, 1, - 0xBF52, framelen[0], - 0xBF53, framelen[1], - 0xBF54, framelen[2], - 0xBF55, framelen[3], - 0, 0, - }; - - return copy_packages(code, pack, 6, space); -} - -static int config_package(struct go7007 *go, __le16 *code, int space) -{ - int fps = go->sensor_framerate / go->fps_scale / 1000; - int rows = go->interlace_coding ? go->height / 32 : go->height / 16; - int brc_window_size = fps; - int q_min = 2, q_max = 31; - int THACCoeffSet0 = 0; - u16 pack[] = { - 0x200e, 0x0000, - 0xc002, 0x14b4, - 0xc003, 0x28b4, - 0xc004, 0x3c5a, - 0xdc05, 0x2a77, - 0xc6c3, go->format == GO7007_FORMAT_MPEG4 ? 0 : - (go->format == GO7007_FORMAT_H263 ? 0 : 1), - 0xc680, go->format == GO7007_FORMAT_MPEG4 ? 0xf1 : - (go->format == GO7007_FORMAT_H263 ? 0x61 : - 0xd3), - 0xc780, 0x0140, - 0xe009, 0x0001, - 0xc60f, 0x0008, - 0xd4ff, 0x0002, - 0xe403, 2340, - 0xe406, 75, - 0xd411, 0x0001, - 0xd410, 0xa1d6, - 0x0001, 0x2801, - - 0x200d, 0x0000, - 0xe402, 0x018b, - 0xe401, 0x8b01, - 0xd472, (go->board_info->sensor_flags & - GO7007_SENSOR_TV) && - (!go->interlace_coding) ? - 0x01b0 : 0x0170, - 0xd475, (go->board_info->sensor_flags & - GO7007_SENSOR_TV) && - (!go->interlace_coding) ? - 0x0008 : 0x0009, - 0xc404, go->interlace_coding ? 0x44 : - (go->format == GO7007_FORMAT_MPEG4 ? 0x11 : - (go->format == GO7007_FORMAT_MPEG1 ? 0x02 : - (go->format == GO7007_FORMAT_MPEG2 ? 0x04 : - (go->format == GO7007_FORMAT_H263 ? 0x08 : - 0x20)))), - 0xbf0a, (go->format == GO7007_FORMAT_MPEG4 ? 8 : - (go->format == GO7007_FORMAT_MPEG1 ? 1 : - (go->format == GO7007_FORMAT_MPEG2 ? 2 : - (go->format == GO7007_FORMAT_H263 ? 4 : 16)))) | - ((go->repeat_seqhead ? 1 : 0) << 6) | - ((go->dvd_mode ? 1 : 0) << 9) | - ((go->gop_header_enable ? 1 : 0) << 10), - 0xbf0b, 0, - 0xdd5a, go->ipb ? 0x14 : 0x0a, - 0xbf0c, 0, - 0xbf0d, 0, - 0xc683, THACCoeffSet0, - 0xc40a, (go->width << 4) | rows, - 0xe01a, go->board_info->hpi_buffer_cap, - 0, 0, - 0, 0, - - 0x2008, 0, - 0xe402, 0x88, - 0xe401, 0x8f01, - 0xbf6a, 0, - 0xbf6b, 0, - 0xbf6c, 0, - 0xbf6d, 0, - 0xbf6e, 0, - 0xbf6f, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - - 0x200e, 0, - 0xbf66, brc_window_size, - 0xbf67, 0, - 0xbf68, q_min, - 0xbf69, q_max, - 0xbfe0, 0, - 0xbfe1, 0, - 0xbfe2, 0, - 0xbfe3, go->ipb ? 3 : 1, - 0xc031, go->board_info->sensor_flags & - GO7007_SENSOR_VBI ? 1 : 0, - 0xc01c, 0x1f, - 0xdd8c, 0x15, - 0xdd94, 0x15, - 0xdd88, go->ipb ? 0x1401 : 0x0a01, - 0xdd90, go->ipb ? 0x1401 : 0x0a01, - 0, 0, - - 0x200e, 0, - 0xbfe4, 0, - 0xbfe5, 0, - 0xbfe6, 0, - 0xbfe7, fps << 8, - 0xbfe8, 0x3a00, - 0xbfe9, 0, - 0xbfea, 0, - 0xbfeb, 0, - 0xbfec, (go->interlace_coding ? 1 << 15 : 0) | - (go->modet_enable ? 0xa : 0) | - (go->board_info->sensor_flags & - GO7007_SENSOR_VBI ? 1 : 0), - 0xbfed, 0, - 0xbfee, 0, - 0xbfef, 0, - 0xbff0, go->board_info->sensor_flags & - GO7007_SENSOR_TV ? 0xf060 : 0xb060, - 0xbff1, 0, - 0, 0, - }; - - return copy_packages(code, pack, 5, space); -} - -static int seqhead_to_package(struct go7007 *go, __le16 *code, int space, - int (*sequence_header_func)(struct go7007 *go, - unsigned char *buf, int ext)) -{ - int vop_time_increment_bitlength = vti_bitlen(go); - int fps = go->sensor_framerate / go->fps_scale * - (go->interlace_coding ? 2 : 1); - unsigned char buf[40] = { }; - int len = sequence_header_func(go, buf, 1); - u16 pack[] = { - 0x2006, 0, - 0xbf08, fps, - 0xbf09, 0, - 0xbff2, vop_time_increment_bitlength, - 0xbff3, (1 << vop_time_increment_bitlength) - 1, - 0xbfe6, 0, - 0xbfe7, (fps / 1000) << 8, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - - 0x2007, 0, - 0xc800, buf[2] << 8 | buf[3], - 0xc801, buf[4] << 8 | buf[5], - 0xc802, buf[6] << 8 | buf[7], - 0xc803, buf[8] << 8 | buf[9], - 0xc406, 64, - 0xc407, len - 64, - 0xc61b, 1, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - - 0x200e, 0, - 0xc808, buf[10] << 8 | buf[11], - 0xc809, buf[12] << 8 | buf[13], - 0xc80a, buf[14] << 8 | buf[15], - 0xc80b, buf[16] << 8 | buf[17], - 0xc80c, buf[18] << 8 | buf[19], - 0xc80d, buf[20] << 8 | buf[21], - 0xc80e, buf[22] << 8 | buf[23], - 0xc80f, buf[24] << 8 | buf[25], - 0xc810, buf[26] << 8 | buf[27], - 0xc811, buf[28] << 8 | buf[29], - 0xc812, buf[30] << 8 | buf[31], - 0xc813, buf[32] << 8 | buf[33], - 0xc814, buf[34] << 8 | buf[35], - 0xc815, buf[36] << 8 | buf[37], - 0, 0, - 0, 0, - 0, 0, - }; - - return copy_packages(code, pack, 3, space); -} - -static int relative_prime(int big, int little) -{ - int remainder; - - while (little != 0) { - remainder = big % little; - big = little; - little = remainder; - } - return big; -} - -static int avsync_to_package(struct go7007 *go, __le16 *code, int space) -{ - int arate = go->board_info->audio_rate * 1001 * go->fps_scale; - int ratio = arate / go->sensor_framerate; - int adjratio = ratio * 215 / 100; - int rprime = relative_prime(go->sensor_framerate, - arate % go->sensor_framerate); - int f1 = (arate % go->sensor_framerate) / rprime; - int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime; - u16 pack[] = { - 0x200e, 0, - 0xbf98, (u16)((-adjratio) & 0xffff), - 0xbf99, (u16)((-adjratio) >> 16), - 0xbf92, 0, - 0xbf93, 0, - 0xbff4, f1 > f2 ? f1 : f2, - 0xbff5, f1 < f2 ? f1 : f2, - 0xbff6, f1 < f2 ? ratio : ratio + 1, - 0xbff7, f1 > f2 ? ratio : ratio + 1, - 0xbff8, 0, - 0xbff9, 0, - 0xbffa, adjratio & 0xffff, - 0xbffb, adjratio >> 16, - 0xbf94, 0, - 0xbf95, 0, - 0, 0, - }; - - return copy_packages(code, pack, 1, space); -} - -static int final_package(struct go7007 *go, __le16 *code, int space) -{ - int rows = go->interlace_coding ? go->height / 32 : go->height / 16; - u16 pack[] = { - 0x8000, - 0, - 0, - 0, - 0, - 0, - 0, - 2, - ((go->board_info->sensor_flags & GO7007_SENSOR_TV) && - (!go->interlace_coding) ? - (1 << 14) | (1 << 9) : 0) | - ((go->encoder_subsample ? 1 : 0) << 8) | - (go->board_info->sensor_flags & - GO7007_SENSOR_CONFIG_MASK), - ((go->encoder_v_halve ? 1 : 0) << 14) | - (go->encoder_v_halve ? rows << 9 : rows << 8) | - (go->encoder_h_halve ? 1 << 6 : 0) | - (go->encoder_h_halve ? go->width >> 3 : go->width >> 4), - (1 << 15) | (go->encoder_v_offset << 6) | - (1 << 7) | (go->encoder_h_offset >> 2), - (1 << 6), - 0, - 0, - ((go->fps_scale - 1) << 8) | - (go->board_info->sensor_flags & GO7007_SENSOR_TV ? - (1 << 7) : 0) | - 0x41, - go->ipb ? 0xd4c : 0x36b, - (rows << 8) | (go->width >> 4), - go->format == GO7007_FORMAT_MPEG4 ? 0x0404 : 0, - (1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) | - ((go->closed_gop ? 1 : 0) << 12) | - ((go->format == GO7007_FORMAT_MPEG4 ? 1 : 0) << 11) | - /* (1 << 9) | */ - ((go->ipb ? 3 : 0) << 7) | - ((go->modet_enable ? 1 : 0) << 2) | - ((go->dvd_mode ? 1 : 0) << 1) | 1, - (go->format == GO7007_FORMAT_MPEG1 ? 0x89a0 : - (go->format == GO7007_FORMAT_MPEG2 ? 0x89a0 : - (go->format == GO7007_FORMAT_MJPEG ? 0x89a0 : - (go->format == GO7007_FORMAT_MPEG4 ? 0x8920 : - (go->format == GO7007_FORMAT_H263 ? 0x8920 : 0))))), - go->ipb ? 0x1f15 : 0x1f0b, - go->ipb ? 0x0015 : 0x000b, - go->ipb ? 0xa800 : 0x5800, - 0xffff, - 0x0020 + 0x034b * 0, - 0x0020 + 0x034b * 1, - 0x0020 + 0x034b * 2, - 0x0020 + 0x034b * 3, - 0x0020 + 0x034b * 4, - 0x0020 + 0x034b * 5, - go->ipb ? (go->gop_size / 3) : go->gop_size, - (go->height >> 4) * (go->width >> 4) * 110 / 100, - }; - - return copy_packages(code, pack, 1, space); -} - -static int audio_to_package(struct go7007 *go, __le16 *code, int space) -{ - int clock_config = ((go->board_info->audio_flags & - GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) | - ((go->board_info->audio_flags & - GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) | - (((go->board_info->audio_bclk_div / 4) - 1) << 4) | - (go->board_info->audio_main_div - 1); - u16 pack[] = { - 0x200d, 0, - 0x9002, 0, - 0x9002, 0, - 0x9031, 0, - 0x9032, 0, - 0x9033, 0, - 0x9034, 0, - 0x9035, 0, - 0x9036, 0, - 0x9037, 0, - 0x9040, 0, - 0x9000, clock_config, - 0x9001, (go->board_info->audio_flags & 0xffff) | - (1 << 9), - 0x9000, ((go->board_info->audio_flags & - GO7007_AUDIO_I2S_MASTER ? - 1 : 0) << 10) | - clock_config, - 0, 0, - 0, 0, - 0x2005, 0, - 0x9041, 0, - 0x9042, 256, - 0x9043, 0, - 0x9044, 16, - 0x9045, 16, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - 0, 0, - }; - - return copy_packages(code, pack, 2, space); -} - -static int modet_to_package(struct go7007 *go, __le16 *code, int space) -{ - int ret, mb, i, addr, cnt = 0; - u16 pack[32]; - u16 thresholds[] = { - 0x200e, 0, - 0xbf82, go->modet[0].pixel_threshold, - 0xbf83, go->modet[1].pixel_threshold, - 0xbf84, go->modet[2].pixel_threshold, - 0xbf85, go->modet[3].pixel_threshold, - 0xbf86, go->modet[0].motion_threshold, - 0xbf87, go->modet[1].motion_threshold, - 0xbf88, go->modet[2].motion_threshold, - 0xbf89, go->modet[3].motion_threshold, - 0xbf8a, go->modet[0].mb_threshold, - 0xbf8b, go->modet[1].mb_threshold, - 0xbf8c, go->modet[2].mb_threshold, - 0xbf8d, go->modet[3].mb_threshold, - 0xbf8e, 0, - 0xbf8f, 0, - 0, 0, - }; - - ret = copy_packages(code, thresholds, 1, space); - if (ret < 0) - return -1; - cnt += ret; - - addr = 0xbac0; - memset(pack, 0, 64); - i = 0; - for (mb = 0; mb < 1624; ++mb) { - pack[i * 2 + 3] <<= 2; - pack[i * 2 + 3] |= go->modet_map[mb]; - if (mb % 8 != 7) - continue; - pack[i * 2 + 2] = addr++; - ++i; - if (i == 10 || mb == 1623) { - pack[0] = 0x2000 | i; - ret = copy_packages(code + cnt, pack, 1, space - cnt); - if (ret < 0) - return -1; - cnt += ret; - i = 0; - memset(pack, 0, 64); - } - pack[i * 2 + 3] = 0; - } - - memset(pack, 0, 64); - i = 0; - for (addr = 0xbb90; addr < 0xbbfa; ++addr) { - pack[i * 2 + 2] = addr; - pack[i * 2 + 3] = 0; - ++i; - if (i == 10 || addr == 0xbbf9) { - pack[0] = 0x2000 | i; - ret = copy_packages(code + cnt, pack, 1, space - cnt); - if (ret < 0) - return -1; - cnt += ret; - i = 0; - memset(pack, 0, 64); - } - } - return cnt; -} - -static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, - int *framelen) -{ - switch (type) { - case SPECIAL_FRM_HEAD: - switch (go->format) { - case GO7007_FORMAT_MJPEG: - return gen_mjpeghdr_to_package(go, code, space); - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: - return gen_mpeg1hdr_to_package(go, code, space, - framelen); - case GO7007_FORMAT_MPEG4: - return gen_mpeg4hdr_to_package(go, code, space, - framelen); - } - case SPECIAL_BRC_CTRL: - return brctrl_to_package(go, code, space, framelen); - case SPECIAL_CONFIG: - return config_package(go, code, space); - case SPECIAL_SEQHEAD: - switch (go->format) { - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: - return seqhead_to_package(go, code, space, - mpeg1_sequence_header); - case GO7007_FORMAT_MPEG4: - return seqhead_to_package(go, code, space, - mpeg4_sequence_header); - default: - return 0; - } - case SPECIAL_AV_SYNC: - return avsync_to_package(go, code, space); - case SPECIAL_FINAL: - return final_package(go, code, space); - case SPECIAL_AUDIO: - return audio_to_package(go, code, space); - case SPECIAL_MODET: - return modet_to_package(go, code, space); - } - printk(KERN_ERR - "go7007: firmware file contains unsupported feature %04x\n", - type); - return -1; -} - -int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) -{ - const struct firmware *fw_entry; - __le16 *code, *src; - int framelen[8] = { }; /* holds the lengths of empty frame templates */ - int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags; - int mode_flag; - int ret; - - switch (go->format) { - case GO7007_FORMAT_MJPEG: - mode_flag = FLAG_MODE_MJPEG; - break; - case GO7007_FORMAT_MPEG1: - mode_flag = FLAG_MODE_MPEG1; - break; - case GO7007_FORMAT_MPEG2: - mode_flag = FLAG_MODE_MPEG2; - break; - case GO7007_FORMAT_MPEG4: - mode_flag = FLAG_MODE_MPEG4; - break; - default: - return -1; - } - if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) { - printk(KERN_ERR - "go7007: unable to load firmware from file \"%s\"\n", - go->board_info->firmware); - return -1; - } - code = kzalloc(codespace * 2, GFP_KERNEL); - if (code == NULL) { - printk(KERN_ERR "go7007: unable to allocate %d bytes for " - "firmware construction\n", codespace * 2); - goto fw_failed; - } - src = (__le16 *)fw_entry->data; - srclen = fw_entry->size / 2; - while (srclen >= 2) { - chunk_flags = __le16_to_cpu(src[0]); - chunk_len = __le16_to_cpu(src[1]); - if (chunk_len + 2 > srclen) { - printk(KERN_ERR "go7007: firmware file \"%s\" " - "appears to be corrupted\n", - go->board_info->firmware); - goto fw_failed; - } - if (chunk_flags & mode_flag) { - if (chunk_flags & FLAG_SPECIAL) { - ret = do_special(go, __le16_to_cpu(src[2]), - &code[i], codespace - i, framelen); - if (ret < 0) { - printk(KERN_ERR "go7007: insufficient " - "memory for firmware " - "construction\n"); - goto fw_failed; - } - i += ret; - } else { - if (codespace - i < chunk_len) { - printk(KERN_ERR "go7007: insufficient " - "memory for firmware " - "construction\n"); - goto fw_failed; - } - memcpy(&code[i], &src[2], chunk_len * 2); - i += chunk_len; - } - } - srclen -= chunk_len + 2; - src += chunk_len + 2; - } - release_firmware(fw_entry); - *fw = (u8 *)code; - *fwlen = i * 2; - return 0; - -fw_failed: - kfree(code); - release_firmware(fw_entry); - return -1; -} diff --git a/drivers/staging/go7007/go7007-i2c.c b/drivers/staging/go7007/go7007-i2c.c deleted file mode 100644 index b8cfa1a6eaeb..000000000000 --- a/drivers/staging/go7007/go7007-i2c.c +++ /dev/null @@ -1,225 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" -#include "wis-i2c.h" - -/********************* Driver for on-board I2C adapter *********************/ - -/* #define GO7007_I2C_DEBUG */ - -#define SPI_I2C_ADDR_BASE 0x1400 -#define STATUS_REG_ADDR (SPI_I2C_ADDR_BASE + 0x2) -#define I2C_CTRL_REG_ADDR (SPI_I2C_ADDR_BASE + 0x6) -#define I2C_DEV_UP_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x7) -#define I2C_LO_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x8) -#define I2C_DATA_REG_ADDR (SPI_I2C_ADDR_BASE + 0x9) -#define I2C_CLKFREQ_REG_ADDR (SPI_I2C_ADDR_BASE + 0xa) - -#define I2C_STATE_MASK 0x0007 -#define I2C_READ_READY_MASK 0x0008 - -/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs - * on the Adlink PCI-MPG24, so access is shared between all of them. */ -static DEFINE_MUTEX(adlink_mpg24_i2c_lock); - -static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, - u16 command, int flags, u8 *data) -{ - int i, ret = -1; - u16 val; - - if (go->status == STATUS_SHUTDOWN) - return -1; - -#ifdef GO7007_I2C_DEBUG - if (read) - printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n", - command, addr); - else - printk(KERN_DEBUG - "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", - *data, command, addr); -#endif - - mutex_lock(&go->hw_lock); - - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Bridge the I2C port on this GO7007 to the shared bus */ - mutex_lock(&adlink_mpg24_i2c_lock); - go7007_write_addr(go, 0x3c82, 0x0020); - } - - /* Wait for I2C adapter to be ready */ - for (i = 0; i < 10; ++i) { - if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) - goto i2c_done; - if (!(val & I2C_STATE_MASK)) - break; - msleep(100); - } - if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); - goto i2c_done; - } - - /* Set target register (command) */ - go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags); - go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command); - - /* If we're writing, send the data and target address and we're done */ - if (!read) { - go7007_write_addr(go, I2C_DATA_REG_ADDR, *data); - go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, - (addr << 9) | (command >> 8)); - ret = 0; - goto i2c_done; - } - - /* Otherwise, we're reading. First clear i2c_rx_data_rdy. */ - if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) - goto i2c_done; - - /* Send the target address plus read flag */ - go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, - (addr << 9) | 0x0100 | (command >> 8)); - - /* Wait for i2c_rx_data_rdy */ - for (i = 0; i < 10; ++i) { - if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) - goto i2c_done; - if (val & I2C_READ_READY_MASK) - break; - msleep(100); - } - if (i == 10) { - printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); - goto i2c_done; - } - - /* Retrieve the read byte */ - if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) - goto i2c_done; - *data = val; - ret = 0; - -i2c_done: - if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { - /* Isolate the I2C port on this GO7007 from the shared bus */ - go7007_write_addr(go, 0x3c82, 0x0000); - mutex_unlock(&adlink_mpg24_i2c_lock); - } - mutex_unlock(&go->hw_lock); - return ret; -} - -static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr, - unsigned short flags, char read_write, - u8 command, int size, union i2c_smbus_data *data) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - - if (size != I2C_SMBUS_BYTE_DATA) - return -1; - return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command, - flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte); -} - -/* VERY LIMITED I2C master xfer function -- only needed because the - * SMBus functions only support 8-bit commands and the SAA7135 uses - * 16-bit commands. The I2C interface on the GO7007, as limited as - * it is, does support this mode. */ - -static int go7007_i2c_master_xfer(struct i2c_adapter *adapter, - struct i2c_msg msgs[], int num) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - int i; - - for (i = 0; i < num; ++i) { - /* We can only do two things here -- write three bytes, or - * write two bytes and read one byte. */ - if (msgs[i].len == 2) { - if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr || - (msgs[i].flags & I2C_M_RD) || - !(msgs[i + 1].flags & I2C_M_RD) || - msgs[i + 1].len != 1) - return -1; - if (go7007_i2c_xfer(go, msgs[i].addr, 1, - (msgs[i].buf[0] << 8) | msgs[i].buf[1], - 0x01, &msgs[i + 1].buf[0]) < 0) - return -1; - ++i; - } else if (msgs[i].len == 3) { - if (msgs[i].flags & I2C_M_RD) - return -1; - if (msgs[i].len != 3) - return -1; - if (go7007_i2c_xfer(go, msgs[i].addr, 0, - (msgs[i].buf[0] << 8) | msgs[i].buf[1], - 0x01, &msgs[i].buf[2]) < 0) - return -1; - } else - return -1; - } - - return 0; -} - -static u32 go7007_functionality(struct i2c_adapter *adapter) -{ - return I2C_FUNC_SMBUS_BYTE_DATA; -} - -static struct i2c_algorithm go7007_algo = { - .smbus_xfer = go7007_smbus_xfer, - .master_xfer = go7007_i2c_master_xfer, - .functionality = go7007_functionality, -}; - -static struct i2c_adapter go7007_adap_templ = { - .owner = THIS_MODULE, - .name = "WIS GO7007SB", - .algo = &go7007_algo, -}; - -int go7007_i2c_init(struct go7007 *go) -{ - memcpy(&go->i2c_adapter, &go7007_adap_templ, - sizeof(go7007_adap_templ)); - go->i2c_adapter.dev.parent = go->dev; - i2c_set_adapdata(&go->i2c_adapter, go); - if (i2c_add_adapter(&go->i2c_adapter) < 0) { - printk(KERN_ERR - "go7007-i2c: error: i2c_add_adapter failed\n"); - return -1; - } - return 0; -} diff --git a/drivers/staging/go7007/go7007-priv.h b/drivers/staging/go7007/go7007-priv.h deleted file mode 100644 index b58c394c6555..000000000000 --- a/drivers/staging/go7007/go7007-priv.h +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -/* - * This is the private include file for the go7007 driver. It should not - * be included by anybody but the driver itself, and especially not by - * user-space applications. - */ - -#include - -struct go7007; - -/* IDs to activate board-specific support code */ -#define GO7007_BOARDID_MATRIX_II 0 -#define GO7007_BOARDID_MATRIX_RELOAD 1 -#define GO7007_BOARDID_STAR_TREK 2 -#define GO7007_BOARDID_PCI_VOYAGER 3 -#define GO7007_BOARDID_XMEN 4 -#define GO7007_BOARDID_XMEN_II 5 -#define GO7007_BOARDID_XMEN_III 6 -#define GO7007_BOARDID_MATRIX_REV 7 -#define GO7007_BOARDID_PX_M402U 16 -#define GO7007_BOARDID_PX_TV402U_ANY 17 /* need to check tuner model */ -#define GO7007_BOARDID_PX_TV402U_NA 18 /* detected NTSC tuner */ -#define GO7007_BOARDID_PX_TV402U_EU 19 /* detected PAL tuner */ -#define GO7007_BOARDID_PX_TV402U_JP 20 /* detected NTSC-J tuner */ -#define GO7007_BOARDID_LIFEVIEW_LR192 21 /* TV Walker Ultra */ -#define GO7007_BOARDID_ENDURA 22 -#define GO7007_BOARDID_ADLINK_MPG24 23 -#define GO7007_BOARDID_SENSORAY_2250 24 /* Sensoray 2250/2251 */ - -/* Various characteristics of each board */ -#define GO7007_BOARD_HAS_AUDIO (1<<0) -#define GO7007_BOARD_USE_ONBOARD_I2C (1<<1) -#define GO7007_BOARD_HAS_TUNER (1<<2) - -/* Characteristics of sensor devices */ -#define GO7007_SENSOR_VALID_POLAR (1<<0) -#define GO7007_SENSOR_HREF_POLAR (1<<1) -#define GO7007_SENSOR_VREF_POLAR (1<<2) -#define GO7007_SENSOR_FIELD_ID_POLAR (1<<3) -#define GO7007_SENSOR_BIT_WIDTH (1<<4) -#define GO7007_SENSOR_VALID_ENABLE (1<<5) -#define GO7007_SENSOR_656 (1<<6) -#define GO7007_SENSOR_CONFIG_MASK 0x7f -#define GO7007_SENSOR_TV (1<<7) -#define GO7007_SENSOR_VBI (1<<8) -#define GO7007_SENSOR_SCALING (1<<9) - -/* Characteristics of audio sensor devices */ -#define GO7007_AUDIO_I2S_MODE_1 (1) -#define GO7007_AUDIO_I2S_MODE_2 (2) -#define GO7007_AUDIO_I2S_MODE_3 (3) -#define GO7007_AUDIO_BCLK_POLAR (1<<2) -#define GO7007_AUDIO_WORD_14 (14<<4) -#define GO7007_AUDIO_WORD_16 (16<<4) -#define GO7007_AUDIO_ONE_CHANNEL (1<<11) -#define GO7007_AUDIO_I2S_MASTER (1<<16) -#define GO7007_AUDIO_OKI_MODE (1<<17) - -struct go7007_board_info { - char *firmware; - unsigned int flags; - int hpi_buffer_cap; - unsigned int sensor_flags; - int sensor_width; - int sensor_height; - int sensor_framerate; - int sensor_h_offset; - int sensor_v_offset; - unsigned int audio_flags; - int audio_rate; - int audio_bclk_div; - int audio_main_div; - int num_i2c_devs; - struct { - const char *type; - int id; - int addr; - } i2c_devs[4]; - int num_inputs; - struct { - int video_input; - int audio_input; - char *name; - } inputs[4]; -}; - -struct go7007_hpi_ops { - int (*interface_reset)(struct go7007 *go); - int (*write_interrupt)(struct go7007 *go, int addr, int data); - int (*read_interrupt)(struct go7007 *go); - int (*stream_start)(struct go7007 *go); - int (*stream_stop)(struct go7007 *go); - int (*send_firmware)(struct go7007 *go, u8 *data, int len); - int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg); -}; - -/* The video buffer size must be a multiple of PAGE_SIZE */ -#define GO7007_BUF_PAGES (128 * 1024 / PAGE_SIZE) -#define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT) - -struct go7007_buffer { - struct go7007 *go; /* Reverse reference for VMA ops */ - int index; /* Reverse reference for DQBUF */ - enum { BUF_STATE_IDLE, BUF_STATE_QUEUED, BUF_STATE_DONE } state; - u32 seq; - struct timeval timestamp; - struct list_head stream; - struct page *pages[GO7007_BUF_PAGES + 1]; /* extra for straddling */ - unsigned long user_addr; - unsigned int page_count; - unsigned int offset; - unsigned int bytesused; - unsigned int frame_offset; - u32 modet_active; - int mapped; -}; - -struct go7007_file { - struct go7007 *go; - struct mutex lock; - int buf_count; - struct go7007_buffer *bufs; -}; - -#define GO7007_FORMAT_MJPEG 0 -#define GO7007_FORMAT_MPEG4 1 -#define GO7007_FORMAT_MPEG1 2 -#define GO7007_FORMAT_MPEG2 3 -#define GO7007_FORMAT_H263 4 - -#define GO7007_RATIO_1_1 0 -#define GO7007_RATIO_4_3 1 -#define GO7007_RATIO_16_9 2 - -enum go7007_parser_state { - STATE_DATA, - STATE_00, - STATE_00_00, - STATE_00_00_01, - STATE_FF, - STATE_VBI_LEN_A, - STATE_VBI_LEN_B, - STATE_MODET_MAP, - STATE_UNPARSED, -}; - -struct go7007 { - struct device *dev; - struct go7007_board_info *board_info; - unsigned int board_id; - int tuner_type; - int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */ - char name[64]; - struct video_device *video_dev; - struct v4l2_device v4l2_dev; - int ref_count; - enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; - spinlock_t spinlock; - struct mutex hw_lock; - int streaming; - int in_use; - int audio_enabled; - - /* Video input */ - int input; - enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard; - int sensor_framerate; - int width; - int height; - int encoder_h_offset; - int encoder_v_offset; - unsigned int encoder_h_halve:1; - unsigned int encoder_v_halve:1; - unsigned int encoder_subsample:1; - - /* Encoder config */ - int format; - int bitrate; - int fps_scale; - int pali; - int aspect_ratio; - int gop_size; - unsigned int ipb:1; - unsigned int closed_gop:1; - unsigned int repeat_seqhead:1; - unsigned int seq_header_enable:1; - unsigned int gop_header_enable:1; - unsigned int dvd_mode:1; - unsigned int interlace_coding:1; - - /* Motion detection */ - unsigned int modet_enable:1; - struct { - unsigned int enable:1; - int pixel_threshold; - int motion_threshold; - int mb_threshold; - } modet[4]; - unsigned char modet_map[1624]; - unsigned char active_map[216]; - - /* Video streaming */ - struct go7007_buffer *active_buf; - enum go7007_parser_state state; - int parse_length; - u16 modet_word; - int seen_frame; - u32 next_seq; - struct list_head stream; - wait_queue_head_t frame_waitq; - - /* Audio streaming */ - void (*audio_deliver)(struct go7007 *go, u8 *buf, int length); - void *snd_context; - - /* I2C */ - int i2c_adapter_online; - struct i2c_adapter i2c_adapter; - - /* HPI driver */ - struct go7007_hpi_ops *hpi_ops; - void *hpi_context; - int interrupt_available; - wait_queue_head_t interrupt_waitq; - unsigned short interrupt_value; - unsigned short interrupt_data; -}; - -static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct go7007, v4l2_dev); -} - -/* All of these must be called with the hpi_lock mutex held! */ -#define go7007_interface_reset(go) \ - ((go)->hpi_ops->interface_reset(go)) -#define go7007_write_interrupt(go, x, y) \ - ((go)->hpi_ops->write_interrupt)((go), (x), (y)) -#define go7007_stream_start(go) \ - ((go)->hpi_ops->stream_start(go)) -#define go7007_stream_stop(go) \ - ((go)->hpi_ops->stream_stop(go)) -#define go7007_send_firmware(go, x, y) \ - ((go)->hpi_ops->send_firmware)((go), (x), (y)) -#define go7007_write_addr(go, x, y) \ - ((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y)) - -/* go7007-driver.c */ -int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data); -int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data); -int go7007_boot_encoder(struct go7007 *go, int init_i2c); -int go7007_reset_encoder(struct go7007 *go); -int go7007_register_encoder(struct go7007 *go); -int go7007_start_encoder(struct go7007 *go); -void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length); -struct go7007 *go7007_alloc(struct go7007_board_info *board, - struct device *dev); -void go7007_remove(struct go7007 *go); - -/* go7007-fw.c */ -int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen); - -/* go7007-i2c.c */ -int go7007_i2c_init(struct go7007 *go); -int go7007_i2c_remove(struct go7007 *go); - -/* go7007-v4l2.c */ -int go7007_v4l2_init(struct go7007 *go); -void go7007_v4l2_remove(struct go7007 *go); - -/* snd-go7007.c */ -int go7007_snd_init(struct go7007 *go); -int go7007_snd_remove(struct go7007 *go); diff --git a/drivers/staging/go7007/go7007-usb.c b/drivers/staging/go7007/go7007-usb.c deleted file mode 100644 index 3db3b0a91cc1..000000000000 --- a/drivers/staging/go7007/go7007-usb.c +++ /dev/null @@ -1,1288 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" -#include "wis-i2c.h" - -static unsigned int assume_endura; -module_param(assume_endura, int, 0644); -MODULE_PARM_DESC(assume_endura, "when probing fails, " - "hardware is a Pelco Endura"); - -/* #define GO7007_USB_DEBUG */ -/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */ - -#define HPI_STATUS_ADDR 0xFFF4 -#define INT_PARAM_ADDR 0xFFF6 -#define INT_INDEX_ADDR 0xFFF8 - -/* - * Pipes on EZ-USB interface: - * 0 snd - Control - * 0 rcv - Control - * 2 snd - Download firmware (control) - * 4 rcv - Read Interrupt (interrupt) - * 6 rcv - Read Video (bulk) - * 8 rcv - Read Audio (bulk) - */ - -#define GO7007_USB_EZUSB (1<<0) -#define GO7007_USB_EZUSB_I2C (1<<1) - -struct go7007_usb_board { - unsigned int flags; - struct go7007_board_info main_info; -}; - -struct go7007_usb { - struct go7007_usb_board *board; - struct mutex i2c_lock; - struct usb_device *usbdev; - struct urb *video_urbs[8]; - struct urb *audio_urbs[8]; - struct urb *intr_urb; -}; - -/*********************** Product specification data ***********************/ - -static struct go7007_usb_board board_matrix_ii = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "wis_saa7115", - .id = I2C_DRIVERID_WIS_SAA7115, - .addr = 0x20, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - { - .video_input = 9, - .name = "S-Video", - }, - }, - }, -}; - -static struct go7007_usb_board board_matrix_reload = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "wis_saa7113", - .id = I2C_DRIVERID_WIS_SAA7113, - .addr = 0x25, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - { - .video_input = 9, - .name = "S-Video", - }, - }, - }, -}; - -static struct go7007_usb_board board_star_trek = { - .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_HAS_AUDIO, /* | - GO7007_BOARD_HAS_TUNER, */ - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "wis_saa7115", - .id = I2C_DRIVERID_WIS_SAA7115, - .addr = 0x20, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 1, - /* .audio_input = AUDIO_EXTERN, */ - .name = "Composite", - }, - { - .video_input = 8, - /* .audio_input = AUDIO_EXTERN, */ - .name = "S-Video", - }, - /* { - * .video_input = 3, - * .audio_input = AUDIO_TUNER, - * .name = "Tuner", - * }, - */ - }, - }, -}; - -static struct go7007_usb_board board_px_tv402u = { - .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_HAS_TUNER, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .num_i2c_devs = 3, - .i2c_devs = { - { - .type = "wis_saa7115", - .id = I2C_DRIVERID_WIS_SAA7115, - .addr = 0x20, - }, - { - .type = "wis_uda1342", - .id = I2C_DRIVERID_WIS_UDA1342, - .addr = 0x1a, - }, - { - .type = "wis_sony_tuner", - .id = I2C_DRIVERID_WIS_SONY_TUNER, - .addr = 0x60, - }, - }, - .num_inputs = 3, - .inputs = { - { - .video_input = 1, - .audio_input = TVAUDIO_INPUT_EXTERN, - .name = "Composite", - }, - { - .video_input = 8, - .audio_input = TVAUDIO_INPUT_EXTERN, - .name = "S-Video", - }, - { - .video_input = 3, - .audio_input = TVAUDIO_INPUT_TUNER, - .name = "Tuner", - }, - }, - }, -}; - -static struct go7007_usb_board board_xmen = { - .flags = 0, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_USE_ONBOARD_I2C, - .hpi_buffer_cap = 0, - .sensor_flags = GO7007_SENSOR_VREF_POLAR, - .sensor_width = 320, - .sensor_height = 240, - .sensor_framerate = 30030, - .audio_flags = GO7007_AUDIO_ONE_CHANNEL | - GO7007_AUDIO_I2S_MODE_3 | - GO7007_AUDIO_WORD_14 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_BCLK_POLAR | - GO7007_AUDIO_OKI_MODE, - .audio_rate = 8000, - .audio_bclk_div = 48, - .audio_main_div = 1, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "wis_ov7640", - .id = I2C_DRIVERID_WIS_OV7640, - .addr = 0x21, - }, - }, - .num_inputs = 1, - .inputs = { - { - .name = "Camera", - }, - }, - }, -}; - -static struct go7007_usb_board board_matrix_revolution = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "wis_tw9903", - .id = I2C_DRIVERID_WIS_TW9903, - .addr = 0x44, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 2, - .name = "Composite", - }, - { - .video_input = 8, - .name = "S-Video", - }, - }, - }, -}; - -static struct go7007_usb_board board_lifeview_lr192 = { - .flags = GO7007_USB_EZUSB, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_HAS_AUDIO | - GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI | - GO7007_SENSOR_SCALING, - .num_i2c_devs = 0, - .num_inputs = 1, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - }, - }, -}; - -static struct go7007_usb_board board_endura = { - .flags = 0, - .main_info = { - .firmware = "go7007tv.bin", - .flags = 0, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 8000, - .audio_bclk_div = 48, - .audio_main_div = 8, - .hpi_buffer_cap = 0, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV, - .sensor_h_offset = 8, - .num_i2c_devs = 0, - .num_inputs = 1, - .inputs = { - { - .name = "Camera", - }, - }, - }, -}; - -static struct go7007_usb_board board_adlink_mpg24 = { - .flags = 0, - .main_info = { - .firmware = "go7007tv.bin", - .flags = GO7007_BOARD_USE_ONBOARD_I2C, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 0, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "wis_tw2804", - .id = I2C_DRIVERID_WIS_TW2804, - .addr = 0x00, /* yes, really */ - }, - }, - .num_inputs = 1, - .inputs = { - { - .name = "Composite", - }, - }, - }, -}; - -static struct go7007_usb_board board_sensoray_2250 = { - .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, - .main_info = { - .firmware = "go7007tv.bin", - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_I2S_MASTER | - GO7007_AUDIO_WORD_16, - .flags = GO7007_BOARD_HAS_AUDIO, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_TV, - .num_i2c_devs = 1, - .i2c_devs = { - { - .type = "s2250", - .id = I2C_DRIVERID_S2250, - .addr = 0x43, - }, - }, - .num_inputs = 2, - .inputs = { - { - .video_input = 0, - .name = "Composite", - }, - { - .video_input = 1, - .name = "S-Video", - }, - }, - }, -}; - -MODULE_FIRMWARE("go7007tv.bin"); - -static const struct usb_device_id go7007_usb_id_table[] = { - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x200, /* Revision number of XMen */ - .bcdDevice_hi = 0x200, - .bInterfaceClass = 255, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 255, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x202, /* Revision number of Matrix II */ - .bcdDevice_hi = 0x202, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_II, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x204, /* Revision number of Matrix */ - .bcdDevice_hi = 0x204, /* Reloaded */ - .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x205, /* Revision number of XMen-II */ - .bcdDevice_hi = 0x205, - .bInterfaceClass = 255, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 255, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_II, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x208, /* Revision number of Star Trek */ - .bcdDevice_hi = 0x208, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_STAR_TREK, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | - USB_DEVICE_ID_MATCH_INT_INFO, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x209, /* Revision number of XMen-III */ - .bcdDevice_hi = 0x209, - .bInterfaceClass = 255, - .bInterfaceSubClass = 0, - .bInterfaceProtocol = 255, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_III, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ - .idProduct = 0x7007, /* Product ID of GO7007SB chip */ - .bcdDevice_lo = 0x210, /* Revision number of Matrix */ - .bcdDevice_hi = 0x210, /* Revolution */ - .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x093b, /* Vendor ID of Plextor */ - .idProduct = 0xa102, /* Product ID of M402U */ - .bcdDevice_lo = 0x1, /* revision number of Blueberry */ - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_M402U, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x093b, /* Vendor ID of Plextor */ - .idProduct = 0xa104, /* Product ID of TV402U */ - .bcdDevice_lo = 0x1, - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U_ANY, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x10fd, /* Vendor ID of Anubis Electronics */ - .idProduct = 0xde00, /* Product ID of Lifeview LR192 */ - .bcdDevice_lo = 0x1, - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192, - }, - { - .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, - .idVendor = 0x1943, /* Vendor ID Sensoray */ - .idProduct = 0x2250, /* Product ID of 2250/2251 */ - .bcdDevice_lo = 0x1, - .bcdDevice_hi = 0x1, - .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250, - }, - { } /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, go7007_usb_id_table); - -/********************* Driver for EZ-USB HPI interface *********************/ - -static int go7007_usb_vendor_request(struct go7007 *go, int request, - int value, int index, void *transfer_buffer, int length, int in) -{ - struct go7007_usb *usb = go->hpi_context; - int timeout = 5000; - - if (in) { - return usb_control_msg(usb->usbdev, - usb_rcvctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - value, index, transfer_buffer, length, timeout); - } else { - return usb_control_msg(usb->usbdev, - usb_sndctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, transfer_buffer, length, timeout); - } -} - -static int go7007_usb_interface_reset(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - u16 intr_val, intr_data; - - /* Reset encoder */ - if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) - return -1; - msleep(100); - - if (usb->board->flags & GO7007_USB_EZUSB) { - /* Reset buffer in EZ-USB */ -#ifdef GO7007_USB_DEBUG - printk(KERN_DEBUG "go7007-usb: resetting EZ-USB buffers\n"); -#endif - if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 || - go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0) - return -1; - - /* Reset encoder again */ - if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) - return -1; - msleep(100); - } - - /* Wait for an interrupt to indicate successful hardware reset */ - if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || - (intr_val & ~0x1) != 0x55aa) { - printk(KERN_ERR - "go7007-usb: unable to reset the USB interface\n"); - return -1; - } - return 0; -} - -static int go7007_usb_ezusb_write_interrupt(struct go7007 *go, - int addr, int data) -{ - struct go7007_usb *usb = go->hpi_context; - int i, r; - u16 status_reg; - int timeout = 500; - -#ifdef GO7007_USB_DEBUG - printk(KERN_DEBUG - "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data); -#endif - - for (i = 0; i < 100; ++i) { - r = usb_control_msg(usb->usbdev, - usb_rcvctrlpipe(usb->usbdev, 0), 0x14, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - 0, HPI_STATUS_ADDR, &status_reg, - sizeof(status_reg), timeout); - if (r < 0) - goto write_int_error; - __le16_to_cpus(&status_reg); - if (!(status_reg & 0x0010)) - break; - msleep(10); - } - if (i == 100) { - printk(KERN_ERR - "go7007-usb: device is hung, status reg = 0x%04x\n", - status_reg); - return -1; - } - r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, data, - INT_PARAM_ADDR, NULL, 0, timeout); - if (r < 0) - goto write_int_error; - r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), - 0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr, - INT_INDEX_ADDR, NULL, 0, timeout); - if (r < 0) - goto write_int_error; - return 0; - -write_int_error: - printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r); - return r; -} - -static int go7007_usb_onboard_write_interrupt(struct go7007 *go, - int addr, int data) -{ - struct go7007_usb *usb = go->hpi_context; - u8 *tbuf; - int r; - int timeout = 500; - -#ifdef GO7007_USB_DEBUG - printk(KERN_DEBUG - "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data); -#endif - - tbuf = kzalloc(8, GFP_KERNEL); - if (tbuf == NULL) - return -ENOMEM; - tbuf[0] = data & 0xff; - tbuf[1] = data >> 8; - tbuf[2] = addr & 0xff; - tbuf[3] = addr >> 8; - r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00, - USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa, - 0xf0f0, tbuf, 8, timeout); - kfree(tbuf); - if (r < 0) { - printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r); - return r; - } - return 0; -} - -static void go7007_usb_readinterrupt_complete(struct urb *urb) -{ - struct go7007 *go = (struct go7007 *)urb->context; - u16 *regs = (u16 *)urb->transfer_buffer; - int status = urb->status; - - if (status) { - if (status != -ESHUTDOWN && - go->status != STATUS_SHUTDOWN) { - printk(KERN_ERR - "go7007-usb: error in read interrupt: %d\n", - urb->status); - } else { - wake_up(&go->interrupt_waitq); - return; - } - } else if (urb->actual_length != urb->transfer_buffer_length) { - printk(KERN_ERR "go7007-usb: short read in interrupt pipe!\n"); - } else { - go->interrupt_available = 1; - go->interrupt_data = __le16_to_cpu(regs[0]); - go->interrupt_value = __le16_to_cpu(regs[1]); -#ifdef GO7007_USB_DEBUG - printk(KERN_DEBUG "go7007-usb: ReadInterrupt: %04x %04x\n", - go->interrupt_value, go->interrupt_data); -#endif - } - - wake_up(&go->interrupt_waitq); -} - -static int go7007_usb_read_interrupt(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - int r; - - r = usb_submit_urb(usb->intr_urb, GFP_KERNEL); - if (r < 0) { - printk(KERN_ERR - "go7007-usb: unable to submit interrupt urb: %d\n", r); - return r; - } - return 0; -} - -static void go7007_usb_read_video_pipe_complete(struct urb *urb) -{ - struct go7007 *go = (struct go7007 *)urb->context; - int r, status = urb->status; - - if (!go->streaming) { - wake_up_interruptible(&go->frame_waitq); - return; - } - if (status) { - printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", - status); - return; - } - if (urb->actual_length != urb->transfer_buffer_length) { - printk(KERN_ERR "go7007-usb: short read in video pipe!\n"); - return; - } - go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length); - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r < 0) - printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", r); -} - -static void go7007_usb_read_audio_pipe_complete(struct urb *urb) -{ - struct go7007 *go = (struct go7007 *)urb->context; - int r, status = urb->status; - - if (!go->streaming) - return; - if (status) { - printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", - status); - return; - } - if (urb->actual_length != urb->transfer_buffer_length) { - printk(KERN_ERR "go7007-usb: short read in audio pipe!\n"); - return; - } - if (go->audio_deliver != NULL) - go->audio_deliver(go, urb->transfer_buffer, urb->actual_length); - r = usb_submit_urb(urb, GFP_ATOMIC); - if (r < 0) - printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", r); -} - -static int go7007_usb_stream_start(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - int i, r; - - for (i = 0; i < 8; ++i) { - r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL); - if (r < 0) { - printk(KERN_ERR "go7007-usb: error submitting video " - "urb %d: %d\n", i, r); - goto video_submit_failed; - } - } - if (!go->audio_enabled) - return 0; - - for (i = 0; i < 8; ++i) { - r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL); - if (r < 0) { - printk(KERN_ERR "go7007-usb: error submitting audio " - "urb %d: %d\n", i, r); - goto audio_submit_failed; - } - } - return 0; - -audio_submit_failed: - for (i = 0; i < 7; ++i) - usb_kill_urb(usb->audio_urbs[i]); -video_submit_failed: - for (i = 0; i < 8; ++i) - usb_kill_urb(usb->video_urbs[i]); - return -1; -} - -static int go7007_usb_stream_stop(struct go7007 *go) -{ - struct go7007_usb *usb = go->hpi_context; - int i; - - if (go->status == STATUS_SHUTDOWN) - return 0; - for (i = 0; i < 8; ++i) - usb_kill_urb(usb->video_urbs[i]); - if (go->audio_enabled) - for (i = 0; i < 8; ++i) - usb_kill_urb(usb->audio_urbs[i]); - return 0; -} - -static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len) -{ - struct go7007_usb *usb = go->hpi_context; - int transferred, pipe; - int timeout = 500; - -#ifdef GO7007_USB_DEBUG - printk(KERN_DEBUG "go7007-usb: DownloadBuffer sending %d bytes\n", len); -#endif - - if (usb->board->flags & GO7007_USB_EZUSB) - pipe = usb_sndbulkpipe(usb->usbdev, 2); - else - pipe = usb_sndbulkpipe(usb->usbdev, 3); - - return usb_bulk_msg(usb->usbdev, pipe, data, len, - &transferred, timeout); -} - -static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = { - .interface_reset = go7007_usb_interface_reset, - .write_interrupt = go7007_usb_ezusb_write_interrupt, - .read_interrupt = go7007_usb_read_interrupt, - .stream_start = go7007_usb_stream_start, - .stream_stop = go7007_usb_stream_stop, - .send_firmware = go7007_usb_send_firmware, -}; - -static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = { - .interface_reset = go7007_usb_interface_reset, - .write_interrupt = go7007_usb_onboard_write_interrupt, - .read_interrupt = go7007_usb_read_interrupt, - .stream_start = go7007_usb_stream_start, - .stream_stop = go7007_usb_stream_stop, - .send_firmware = go7007_usb_send_firmware, -}; - -/********************* Driver for EZ-USB I2C adapter *********************/ - -static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter, - struct i2c_msg msgs[], int num) -{ - struct go7007 *go = i2c_get_adapdata(adapter); - struct go7007_usb *usb = go->hpi_context; - u8 buf[16]; - int buf_len, i; - int ret = -1; - - if (go->status == STATUS_SHUTDOWN) - return -1; - - mutex_lock(&usb->i2c_lock); - - for (i = 0; i < num; ++i) { - /* The hardware command is "write some bytes then read some - * bytes", so we try to coalesce a write followed by a read - * into a single USB transaction */ - if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr && - !(msgs[i].flags & I2C_M_RD) && - (msgs[i + 1].flags & I2C_M_RD)) { -#ifdef GO7007_I2C_DEBUG - printk(KERN_DEBUG "go7007-usb: i2c write/read %d/%d " - "bytes on %02x\n", msgs[i].len, - msgs[i + 1].len, msgs[i].addr); -#endif - buf[0] = 0x01; - buf[1] = msgs[i].len + 1; - buf[2] = msgs[i].addr << 1; - memcpy(&buf[3], msgs[i].buf, msgs[i].len); - buf_len = msgs[i].len + 3; - buf[buf_len++] = msgs[++i].len; - } else if (msgs[i].flags & I2C_M_RD) { -#ifdef GO7007_I2C_DEBUG - printk(KERN_DEBUG "go7007-usb: i2c read %d " - "bytes on %02x\n", msgs[i].len, - msgs[i].addr); -#endif - buf[0] = 0x01; - buf[1] = 1; - buf[2] = msgs[i].addr << 1; - buf[3] = msgs[i].len; - buf_len = 4; - } else { -#ifdef GO7007_I2C_DEBUG - printk(KERN_DEBUG "go7007-usb: i2c write %d " - "bytes on %02x\n", msgs[i].len, - msgs[i].addr); -#endif - buf[0] = 0x00; - buf[1] = msgs[i].len + 1; - buf[2] = msgs[i].addr << 1; - memcpy(&buf[3], msgs[i].buf, msgs[i].len); - buf_len = msgs[i].len + 3; - buf[buf_len++] = 0; - } - if (go7007_usb_vendor_request(go, 0x24, 0, 0, - buf, buf_len, 0) < 0) - goto i2c_done; - if (msgs[i].flags & I2C_M_RD) { - memset(buf, 0, sizeof(buf)); - if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf, - msgs[i].len + 1, 1) < 0) - goto i2c_done; - memcpy(msgs[i].buf, buf + 1, msgs[i].len); - } - } - ret = 0; - -i2c_done: - mutex_unlock(&usb->i2c_lock); - return ret; -} - -static u32 go7007_usb_functionality(struct i2c_adapter *adapter) -{ - /* No errors are reported by the hardware, so we don't bother - * supporting quick writes to avoid confusing probing */ - return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK; -} - -static struct i2c_algorithm go7007_usb_algo = { - .master_xfer = go7007_usb_i2c_master_xfer, - .functionality = go7007_usb_functionality, -}; - -static struct i2c_adapter go7007_usb_adap_templ = { - .owner = THIS_MODULE, - .name = "WIS GO7007SB EZ-USB", - .algo = &go7007_usb_algo, -}; - -/********************* USB add/remove functions *********************/ - -static int go7007_usb_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct go7007 *go; - struct go7007_usb *usb; - struct go7007_usb_board *board; - struct usb_device *usbdev = interface_to_usbdev(intf); - char *name; - int video_pipe, i, v_urb_len; - - printk(KERN_DEBUG "go7007-usb: probing new GO7007 USB board\n"); - - switch (id->driver_info) { - case GO7007_BOARDID_MATRIX_II: - name = "WIS Matrix II or compatible"; - board = &board_matrix_ii; - break; - case GO7007_BOARDID_MATRIX_RELOAD: - name = "WIS Matrix Reloaded or compatible"; - board = &board_matrix_reload; - break; - case GO7007_BOARDID_MATRIX_REV: - name = "WIS Matrix Revolution or compatible"; - board = &board_matrix_revolution; - break; - case GO7007_BOARDID_STAR_TREK: - name = "WIS Star Trek or compatible"; - board = &board_star_trek; - break; - case GO7007_BOARDID_XMEN: - name = "WIS XMen or compatible"; - board = &board_xmen; - break; - case GO7007_BOARDID_XMEN_II: - name = "WIS XMen II or compatible"; - board = &board_xmen; - break; - case GO7007_BOARDID_XMEN_III: - name = "WIS XMen III or compatible"; - board = &board_xmen; - break; - case GO7007_BOARDID_PX_M402U: - name = "Plextor PX-M402U"; - board = &board_matrix_ii; - break; - case GO7007_BOARDID_PX_TV402U_ANY: - name = "Plextor PX-TV402U (unknown tuner)"; - board = &board_px_tv402u; - break; - case GO7007_BOARDID_LIFEVIEW_LR192: - printk(KERN_ERR "go7007-usb: The Lifeview TV Walker Ultra " - "is not supported. Sorry!\n"); - return 0; - name = "Lifeview TV Walker Ultra"; - board = &board_lifeview_lr192; - break; - case GO7007_BOARDID_SENSORAY_2250: - printk(KERN_INFO "Sensoray 2250 found\n"); - name = "Sensoray 2250/2251"; - board = &board_sensoray_2250; - break; - default: - printk(KERN_ERR "go7007-usb: unknown board ID %d!\n", - (unsigned int)id->driver_info); - return 0; - } - - usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL); - if (usb == NULL) - return -ENOMEM; - - /* Allocate the URB and buffer for receiving incoming interrupts */ - usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); - if (usb->intr_urb == NULL) - goto allocfail; - usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL); - if (usb->intr_urb->transfer_buffer == NULL) - goto allocfail; - - go = go7007_alloc(&board->main_info, &intf->dev); - if (go == NULL) - goto allocfail; - usb->board = board; - usb->usbdev = usbdev; - go->board_id = id->driver_info; - strncpy(go->name, name, sizeof(go->name)); - if (board->flags & GO7007_USB_EZUSB) - go->hpi_ops = &go7007_usb_ezusb_hpi_ops; - else - go->hpi_ops = &go7007_usb_onboard_hpi_ops; - go->hpi_context = usb; - usb_fill_int_urb(usb->intr_urb, usb->usbdev, - usb_rcvintpipe(usb->usbdev, 4), - usb->intr_urb->transfer_buffer, 2*sizeof(u16), - go7007_usb_readinterrupt_complete, go, 8); - usb_set_intfdata(intf, &go->v4l2_dev); - - /* Boot the GO7007 */ - if (go7007_boot_encoder(go, go->board_info->flags & - GO7007_BOARD_USE_ONBOARD_I2C) < 0) - goto initfail; - - /* Register the EZ-USB I2C adapter, if we're using it */ - if (board->flags & GO7007_USB_EZUSB_I2C) { - memcpy(&go->i2c_adapter, &go7007_usb_adap_templ, - sizeof(go7007_usb_adap_templ)); - mutex_init(&usb->i2c_lock); - go->i2c_adapter.dev.parent = go->dev; - i2c_set_adapdata(&go->i2c_adapter, go); - if (i2c_add_adapter(&go->i2c_adapter) < 0) { - printk(KERN_ERR - "go7007-usb: error: i2c_add_adapter failed\n"); - goto initfail; - } - go->i2c_adapter_online = 1; - } - - /* Pelco and Adlink reused the XMen and XMen-III vendor and product - * IDs for their own incompatible designs. We can detect XMen boards - * by probing the sensor, but there is no way to probe the sensors on - * the Pelco and Adlink designs so we default to the Adlink. If it - * is actually a Pelco, the user must set the assume_endura module - * parameter. */ - if ((go->board_id == GO7007_BOARDID_XMEN || - go->board_id == GO7007_BOARDID_XMEN_III) && - go->i2c_adapter_online) { - union i2c_smbus_data data; - - /* Check to see if register 0x0A is 0x76 */ - i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB, - I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data); - if (data.byte != 0x76) { - if (assume_endura) { - go->board_id = GO7007_BOARDID_ENDURA; - usb->board = board = &board_endura; - go->board_info = &board->main_info; - strncpy(go->name, "Pelco Endura", - sizeof(go->name)); - } else { - u16 channel; - - /* set GPIO5 to be an output, currently low */ - go7007_write_addr(go, 0x3c82, 0x0000); - go7007_write_addr(go, 0x3c80, 0x00df); - /* read channel number from GPIO[1:0] */ - go7007_read_addr(go, 0x3c81, &channel); - channel &= 0x3; - go->board_id = GO7007_BOARDID_ADLINK_MPG24; - usb->board = board = &board_adlink_mpg24; - go->board_info = &board->main_info; - go->channel_number = channel; - snprintf(go->name, sizeof(go->name), - "Adlink PCI-MPG24, channel #%d", - channel); - } - } - } - - /* Probe the tuner model on the TV402U */ - if (go->board_id == GO7007_BOARDID_PX_TV402U_ANY) { - u8 data[3]; - - /* Board strapping indicates tuner model */ - if (go7007_usb_vendor_request(go, 0x41, 0, 0, data, 3, 1) < 0) { - printk(KERN_ERR "go7007-usb: GPIO read failed!\n"); - goto initfail; - } - switch (data[0] >> 6) { - case 1: - go->board_id = GO7007_BOARDID_PX_TV402U_EU; - go->tuner_type = TUNER_SONY_BTF_PG472Z; - strncpy(go->name, "Plextor PX-TV402U-EU", - sizeof(go->name)); - break; - case 2: - go->board_id = GO7007_BOARDID_PX_TV402U_JP; - go->tuner_type = TUNER_SONY_BTF_PK467Z; - strncpy(go->name, "Plextor PX-TV402U-JP", - sizeof(go->name)); - break; - case 3: - go->board_id = GO7007_BOARDID_PX_TV402U_NA; - go->tuner_type = TUNER_SONY_BTF_PB463Z; - strncpy(go->name, "Plextor PX-TV402U-NA", - sizeof(go->name)); - break; - default: - printk(KERN_DEBUG "go7007-usb: unable to detect " - "tuner type!\n"); - break; - } - /* Configure tuner mode selection inputs connected - * to the EZ-USB GPIO output pins */ - if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0, - NULL, 0, 0) < 0) { - printk(KERN_ERR "go7007-usb: GPIO write failed!\n"); - goto initfail; - } - } - - /* Print a nasty message if the user attempts to use a USB2.0 device in - * a USB1.1 port. There will be silent corruption of the stream. */ - if ((board->flags & GO7007_USB_EZUSB) && - usbdev->speed != USB_SPEED_HIGH) - printk(KERN_ERR "go7007-usb: *** WARNING *** This device " - "must be connected to a USB 2.0 port! " - "Attempting to capture video through a USB 1.1 " - "port will result in stream corruption, even " - "at low bitrates!\n"); - - /* Do any final GO7007 initialization, then register the - * V4L2 and ALSA interfaces */ - if (go7007_register_encoder(go) < 0) - goto initfail; - - /* Allocate the URBs and buffers for receiving the video stream */ - if (board->flags & GO7007_USB_EZUSB) { - v_urb_len = 1024; - video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); - } else { - v_urb_len = 512; - video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); - } - for (i = 0; i < 8; ++i) { - usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); - if (usb->video_urbs[i] == NULL) - goto initfail; - usb->video_urbs[i]->transfer_buffer = - kmalloc(v_urb_len, GFP_KERNEL); - if (usb->video_urbs[i]->transfer_buffer == NULL) - goto initfail; - usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe, - usb->video_urbs[i]->transfer_buffer, v_urb_len, - go7007_usb_read_video_pipe_complete, go); - } - - /* Allocate the URBs and buffers for receiving the audio stream */ - if ((board->flags & GO7007_USB_EZUSB) && go->audio_enabled) - for (i = 0; i < 8; ++i) { - usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); - if (usb->audio_urbs[i] == NULL) - goto initfail; - usb->audio_urbs[i]->transfer_buffer = kmalloc(4096, - GFP_KERNEL); - if (usb->audio_urbs[i]->transfer_buffer == NULL) - goto initfail; - usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev, - usb_rcvbulkpipe(usb->usbdev, 8), - usb->audio_urbs[i]->transfer_buffer, 4096, - go7007_usb_read_audio_pipe_complete, go); - } - - - go->status = STATUS_ONLINE; - return 0; - -initfail: - go->status = STATUS_SHUTDOWN; - return 0; - -allocfail: - if (usb->intr_urb) { - kfree(usb->intr_urb->transfer_buffer); - usb_free_urb(usb->intr_urb); - } - kfree(usb); - return -ENOMEM; -} - -static void go7007_usb_disconnect(struct usb_interface *intf) -{ - struct go7007 *go = to_go7007(usb_get_intfdata(intf)); - struct go7007_usb *usb = go->hpi_context; - struct urb *vurb, *aurb; - int i; - - go->status = STATUS_SHUTDOWN; - usb_kill_urb(usb->intr_urb); - - /* Free USB-related structs */ - for (i = 0; i < 8; ++i) { - vurb = usb->video_urbs[i]; - if (vurb) { - usb_kill_urb(vurb); - kfree(vurb->transfer_buffer); - usb_free_urb(vurb); - } - aurb = usb->audio_urbs[i]; - if (aurb) { - usb_kill_urb(aurb); - kfree(aurb->transfer_buffer); - usb_free_urb(aurb); - } - } - kfree(usb->intr_urb->transfer_buffer); - usb_free_urb(usb->intr_urb); - - kfree(go->hpi_context); - - go7007_remove(go); -} - -static struct usb_driver go7007_usb_driver = { - .name = "go7007", - .probe = go7007_usb_probe, - .disconnect = go7007_usb_disconnect, - .id_table = go7007_usb_id_table, -}; - -static int __init go7007_usb_init(void) -{ - return usb_register(&go7007_usb_driver); -} - -static void __exit go7007_usb_cleanup(void) -{ - usb_deregister(&go7007_usb_driver); -} - -module_init(go7007_usb_init); -module_exit(go7007_usb_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/go7007-v4l2.c b/drivers/staging/go7007/go7007-v4l2.c deleted file mode 100644 index 2b27d8da70a2..000000000000 --- a/drivers/staging/go7007/go7007-v4l2.c +++ /dev/null @@ -1,1839 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007.h" -#include "go7007-priv.h" -#include "wis-i2c.h" - -/* Temporary defines until accepted in v4l-dvb */ -#ifndef V4L2_MPEG_STREAM_TYPE_MPEG_ELEM -#define V4L2_MPEG_STREAM_TYPE_MPEG_ELEM 6 /* MPEG elementary stream */ -#endif -#ifndef V4L2_MPEG_VIDEO_ENCODING_MPEG_4 -#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3 -#endif - -#define call_all(dev, o, f, args...) \ - v4l2_device_call_until_err(dev, 0, o, f, ##args) - -static void deactivate_buffer(struct go7007_buffer *gobuf) -{ - int i; - - if (gobuf->state != BUF_STATE_IDLE) { - list_del(&gobuf->stream); - gobuf->state = BUF_STATE_IDLE; - } - if (gobuf->page_count > 0) { - for (i = 0; i < gobuf->page_count; ++i) - page_cache_release(gobuf->pages[i]); - gobuf->page_count = 0; - } -} - -static void abort_queued(struct go7007 *go) -{ - struct go7007_buffer *gobuf, *next; - - list_for_each_entry_safe(gobuf, next, &go->stream, stream) { - deactivate_buffer(gobuf); - } -} - -static int go7007_streamoff(struct go7007 *go) -{ - int retval = -EINVAL; - unsigned long flags; - - mutex_lock(&go->hw_lock); - if (go->streaming) { - go->streaming = 0; - go7007_stream_stop(go); - spin_lock_irqsave(&go->spinlock, flags); - abort_queued(go); - spin_unlock_irqrestore(&go->spinlock, flags); - go7007_reset_encoder(go); - retval = 0; - } - mutex_unlock(&go->hw_lock); - return 0; -} - -static int go7007_open(struct file *file) -{ - struct go7007 *go = video_get_drvdata(video_devdata(file)); - struct go7007_file *gofh; - - if (go->status != STATUS_ONLINE) - return -EBUSY; - gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL); - if (gofh == NULL) - return -ENOMEM; - ++go->ref_count; - gofh->go = go; - mutex_init(&gofh->lock); - gofh->buf_count = 0; - file->private_data = gofh; - return 0; -} - -static int go7007_release(struct file *file) -{ - struct go7007_file *gofh = file->private_data; - struct go7007 *go = gofh->go; - - if (gofh->buf_count > 0) { - go7007_streamoff(go); - go->in_use = 0; - kfree(gofh->bufs); - gofh->buf_count = 0; - } - kfree(gofh); - if (--go->ref_count == 0) - kfree(go); - file->private_data = NULL; - return 0; -} - -static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format) -{ - u8 *f = page_address(gobuf->pages[0]); - - switch (format) { - case GO7007_FORMAT_MJPEG: - return V4L2_BUF_FLAG_KEYFRAME; - case GO7007_FORMAT_MPEG4: - switch ((f[gobuf->frame_offset + 4] >> 6) & 0x3) { - case 0: - return V4L2_BUF_FLAG_KEYFRAME; - case 1: - return V4L2_BUF_FLAG_PFRAME; - case 2: - return V4L2_BUF_FLAG_BFRAME; - default: - return 0; - } - case GO7007_FORMAT_MPEG1: - case GO7007_FORMAT_MPEG2: - switch ((f[gobuf->frame_offset + 5] >> 3) & 0x7) { - case 1: - return V4L2_BUF_FLAG_KEYFRAME; - case 2: - return V4L2_BUF_FLAG_PFRAME; - case 3: - return V4L2_BUF_FLAG_BFRAME; - default: - return 0; - } - } - - return 0; -} - -static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) -{ - int sensor_height = 0, sensor_width = 0; - int width, height, i; - - if (fmt != NULL && fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && - fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG && - fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG4) - return -EINVAL; - - switch (go->standard) { - case GO7007_STD_NTSC: - sensor_width = 720; - sensor_height = 480; - break; - case GO7007_STD_PAL: - sensor_width = 720; - sensor_height = 576; - break; - case GO7007_STD_OTHER: - sensor_width = go->board_info->sensor_width; - sensor_height = go->board_info->sensor_height; - break; - } - - if (fmt == NULL) { - width = sensor_width; - height = sensor_height; - } else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { - if (fmt->fmt.pix.width > sensor_width) - width = sensor_width; - else if (fmt->fmt.pix.width < 144) - width = 144; - else - width = fmt->fmt.pix.width & ~0x0f; - - if (fmt->fmt.pix.height > sensor_height) - height = sensor_height; - else if (fmt->fmt.pix.height < 96) - height = 96; - else - height = fmt->fmt.pix.height & ~0x0f; - } else { - int requested_size = fmt->fmt.pix.width * fmt->fmt.pix.height; - int sensor_size = sensor_width * sensor_height; - - if (64 * requested_size < 9 * sensor_size) { - width = sensor_width / 4; - height = sensor_height / 4; - } else if (64 * requested_size < 36 * sensor_size) { - width = sensor_width / 2; - height = sensor_height / 2; - } else { - width = sensor_width; - height = sensor_height; - } - width &= ~0xf; - height &= ~0xf; - } - - if (fmt != NULL) { - u32 pixelformat = fmt->fmt.pix.pixelformat; - - memset(fmt, 0, sizeof(*fmt)); - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->fmt.pix.width = width; - fmt->fmt.pix.height = height; - fmt->fmt.pix.pixelformat = pixelformat; - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */ - } - - if (try) - return 0; - - go->width = width; - go->height = height; - go->encoder_h_offset = go->board_info->sensor_h_offset; - go->encoder_v_offset = go->board_info->sensor_v_offset; - for (i = 0; i < 4; ++i) - go->modet[i].enable = 0; - for (i = 0; i < 1624; ++i) - go->modet_map[i] = 0; - - if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { - struct v4l2_mbus_framefmt mbus_fmt; - - mbus_fmt.code = V4L2_MBUS_FMT_FIXED; - if (fmt != NULL) - mbus_fmt.width = fmt->fmt.pix.width; - else - mbus_fmt.width = width; - - if (height > sensor_height / 2) { - mbus_fmt.height = height / 2; - go->encoder_v_halve = 0; - } else { - mbus_fmt.height = height; - go->encoder_v_halve = 1; - } - call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt); - } else { - if (width <= sensor_width / 4) { - go->encoder_h_halve = 1; - go->encoder_v_halve = 1; - go->encoder_subsample = 1; - } else if (width <= sensor_width / 2) { - go->encoder_h_halve = 1; - go->encoder_v_halve = 1; - go->encoder_subsample = 0; - } else { - go->encoder_h_halve = 0; - go->encoder_v_halve = 0; - go->encoder_subsample = 0; - } - } - - if (fmt == NULL) - return 0; - - switch (fmt->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_MPEG: - if (go->format == GO7007_FORMAT_MPEG1 || - go->format == GO7007_FORMAT_MPEG2 || - go->format == GO7007_FORMAT_MPEG4) - break; - go->format = GO7007_FORMAT_MPEG1; - go->pali = 0; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = go->sensor_framerate / 1000; - go->ipb = 0; - go->closed_gop = 1; - go->repeat_seqhead = 1; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 0; - break; - /* Backwards compatibility only! */ - case V4L2_PIX_FMT_MPEG4: - if (go->format == GO7007_FORMAT_MPEG4) - break; - go->format = GO7007_FORMAT_MPEG4; - go->pali = 0xf5; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = go->sensor_framerate / 1000; - go->ipb = 0; - go->closed_gop = 1; - go->repeat_seqhead = 1; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 0; - break; - case V4L2_PIX_FMT_MJPEG: - go->format = GO7007_FORMAT_MJPEG; - go->pali = 0; - go->aspect_ratio = GO7007_RATIO_1_1; - go->gop_size = 0; - go->ipb = 0; - go->closed_gop = 0; - go->repeat_seqhead = 0; - go->seq_header_enable = 0; - go->gop_header_enable = 0; - go->dvd_mode = 0; - break; - } - return 0; -} - -#if 0 -static int clip_to_modet_map(struct go7007 *go, int region, - struct v4l2_clip *clip_list) -{ - struct v4l2_clip clip, *clip_ptr; - int x, y, mbnum; - - /* Check if coordinates are OK and if any macroblocks are already - * used by other regions (besides 0) */ - clip_ptr = clip_list; - while (clip_ptr) { - if (copy_from_user(&clip, clip_ptr, sizeof(clip))) - return -EFAULT; - if (clip.c.left < 0 || (clip.c.left & 0xF) || - clip.c.width <= 0 || (clip.c.width & 0xF)) - return -EINVAL; - if (clip.c.left + clip.c.width > go->width) - return -EINVAL; - if (clip.c.top < 0 || (clip.c.top & 0xF) || - clip.c.height <= 0 || (clip.c.height & 0xF)) - return -EINVAL; - if (clip.c.top + clip.c.height > go->height) - return -EINVAL; - for (y = 0; y < clip.c.height; y += 16) - for (x = 0; x < clip.c.width; x += 16) { - mbnum = (go->width >> 4) * - ((clip.c.top + y) >> 4) + - ((clip.c.left + x) >> 4); - if (go->modet_map[mbnum] != 0 && - go->modet_map[mbnum] != region) - return -EBUSY; - } - clip_ptr = clip.next; - } - - /* Clear old region macroblocks */ - for (mbnum = 0; mbnum < 1624; ++mbnum) - if (go->modet_map[mbnum] == region) - go->modet_map[mbnum] = 0; - - /* Claim macroblocks in this list */ - clip_ptr = clip_list; - while (clip_ptr) { - if (copy_from_user(&clip, clip_ptr, sizeof(clip))) - return -EFAULT; - for (y = 0; y < clip.c.height; y += 16) - for (x = 0; x < clip.c.width; x += 16) { - mbnum = (go->width >> 4) * - ((clip.c.top + y) >> 4) + - ((clip.c.left + x) >> 4); - go->modet_map[mbnum] = region; - } - clip_ptr = clip.next; - } - return 0; -} -#endif - -static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl) -{ - static const u32 mpeg_ctrls[] = { - V4L2_CID_MPEG_CLASS, - V4L2_CID_MPEG_STREAM_TYPE, - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_ASPECT, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, - V4L2_CID_MPEG_VIDEO_BITRATE, - 0 - }; - static const u32 *ctrl_classes[] = { - mpeg_ctrls, - NULL - }; - - ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id); - - switch (ctrl->id) { - case V4L2_CID_MPEG_CLASS: - return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0); - case V4L2_CID_MPEG_STREAM_TYPE: - return v4l2_ctrl_query_fill(ctrl, - V4L2_MPEG_STREAM_TYPE_MPEG2_DVD, - V4L2_MPEG_STREAM_TYPE_MPEG_ELEM, 1, - V4L2_MPEG_STREAM_TYPE_MPEG_ELEM); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill(ctrl, - V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_2); - case V4L2_CID_MPEG_VIDEO_ASPECT: - return v4l2_ctrl_query_fill(ctrl, - V4L2_MPEG_VIDEO_ASPECT_1x1, - V4L2_MPEG_VIDEO_ASPECT_16x9, 1, - V4L2_MPEG_VIDEO_ASPECT_1x1); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(ctrl, 0, 34, 1, 15); - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - return v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); - case V4L2_CID_MPEG_VIDEO_BITRATE: - return v4l2_ctrl_query_fill(ctrl, - 64000, - 10000000, 1, - 1500000); - default: - return -EINVAL; - } - return 0; -} - -static int mpeg_s_ctrl(struct v4l2_control *ctrl, struct go7007 *go) -{ - /* pretty sure we can't change any of these while streaming */ - if (go->streaming) - return -EBUSY; - - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - switch (ctrl->value) { - case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD: - go->format = GO7007_FORMAT_MPEG2; - go->bitrate = 9800000; - go->gop_size = 15; - go->pali = 0x48; - go->closed_gop = 1; - go->repeat_seqhead = 0; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 1; - break; - case V4L2_MPEG_STREAM_TYPE_MPEG_ELEM: - /* todo: */ - break; - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - switch (ctrl->value) { - case V4L2_MPEG_VIDEO_ENCODING_MPEG_1: - go->format = GO7007_FORMAT_MPEG1; - go->pali = 0; - break; - case V4L2_MPEG_VIDEO_ENCODING_MPEG_2: - go->format = GO7007_FORMAT_MPEG2; - /*if (mpeg->pali >> 24 == 2) - go->pali = mpeg->pali & 0xff; - else*/ - go->pali = 0x48; - break; - case V4L2_MPEG_VIDEO_ENCODING_MPEG_4: - go->format = GO7007_FORMAT_MPEG4; - /*if (mpeg->pali >> 24 == 4) - go->pali = mpeg->pali & 0xff; - else*/ - go->pali = 0xf5; - break; - default: - return -EINVAL; - } - go->gop_header_enable = - /*mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER - ? 0 :*/ 1; - /*if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER) - go->repeat_seqhead = 1; - else*/ - go->repeat_seqhead = 0; - go->dvd_mode = 0; - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - if (go->format == GO7007_FORMAT_MJPEG) - return -EINVAL; - switch (ctrl->value) { - case V4L2_MPEG_VIDEO_ASPECT_1x1: - go->aspect_ratio = GO7007_RATIO_1_1; - break; - case V4L2_MPEG_VIDEO_ASPECT_4x3: - go->aspect_ratio = GO7007_RATIO_4_3; - break; - case V4L2_MPEG_VIDEO_ASPECT_16x9: - go->aspect_ratio = GO7007_RATIO_16_9; - break; - case V4L2_MPEG_VIDEO_ASPECT_221x100: - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - if (ctrl->value < 0 || ctrl->value > 34) - return -EINVAL; - go->gop_size = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - if (ctrl->value != 0 && ctrl->value != 1) - return -EINVAL; - go->closed_gop = ctrl->value; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - /* Upper bound is kind of arbitrary here */ - if (ctrl->value < 64000 || ctrl->value > 10000000) - return -EINVAL; - go->bitrate = ctrl->value; - break; - default: - return -EINVAL; - } - return 0; -} - -static int mpeg_g_ctrl(struct v4l2_control *ctrl, struct go7007 *go) -{ - switch (ctrl->id) { - case V4L2_CID_MPEG_STREAM_TYPE: - if (go->dvd_mode) - ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_DVD; - else - ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG_ELEM; - break; - case V4L2_CID_MPEG_VIDEO_ENCODING: - switch (go->format) { - case GO7007_FORMAT_MPEG1: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_1; - break; - case GO7007_FORMAT_MPEG2: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; - break; - case GO7007_FORMAT_MPEG4: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4; - break; - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_ASPECT: - switch (go->aspect_ratio) { - case GO7007_RATIO_1_1: - ctrl->value = V4L2_MPEG_VIDEO_ASPECT_1x1; - break; - case GO7007_RATIO_4_3: - ctrl->value = V4L2_MPEG_VIDEO_ASPECT_4x3; - break; - case GO7007_RATIO_16_9: - ctrl->value = V4L2_MPEG_VIDEO_ASPECT_16x9; - break; - default: - return -EINVAL; - } - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = go->gop_size; - break; - case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: - ctrl->value = go->closed_gop; - break; - case V4L2_CID_MPEG_VIDEO_BITRATE: - ctrl->value = go->bitrate; - break; - default: - return -EINVAL; - } - return 0; -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - strlcpy(cap->driver, "go7007", sizeof(cap->driver)); - strlcpy(cap->card, go->name, sizeof(cap->card)); -#if 0 - strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); -#endif - - cap->version = KERNEL_VERSION(0, 9, 8); - - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */ - - if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) - cap->capabilities |= V4L2_CAP_TUNER; - - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *fmt) -{ - char *desc = NULL; - - switch (fmt->index) { - case 0: - fmt->pixelformat = V4L2_PIX_FMT_MJPEG; - desc = "Motion-JPEG"; - break; - case 1: - fmt->pixelformat = V4L2_PIX_FMT_MPEG; - desc = "MPEG1/MPEG2/MPEG4"; - break; - default: - return -EINVAL; - } - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->flags = V4L2_FMT_FLAG_COMPRESSED; - - strncpy(fmt->description, desc, sizeof(fmt->description)); - - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fmt->fmt.pix.width = go->width; - fmt->fmt.pix.height = go->height; - fmt->fmt.pix.pixelformat = (go->format == GO7007_FORMAT_MJPEG) ? - V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG; - fmt->fmt.pix.field = V4L2_FIELD_NONE; - fmt->fmt.pix.bytesperline = 0; - fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; - fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - return set_capture_size(go, fmt, 1); -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *fmt) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (go->streaming) - return -EBUSY; - - return set_capture_size(go, fmt, 0); -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - int retval = -EBUSY; - unsigned int count, i; - - if (go->streaming) - return retval; - - if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - req->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - mutex_lock(&gofh->lock); - for (i = 0; i < gofh->buf_count; ++i) - if (gofh->bufs[i].mapped > 0) - goto unlock_and_return; - - mutex_lock(&go->hw_lock); - if (go->in_use > 0 && gofh->buf_count == 0) { - mutex_unlock(&go->hw_lock); - goto unlock_and_return; - } - - if (gofh->buf_count > 0) - kfree(gofh->bufs); - - retval = -ENOMEM; - count = req->count; - if (count > 0) { - if (count < 2) - count = 2; - if (count > 32) - count = 32; - - gofh->bufs = kcalloc(count, sizeof(struct go7007_buffer), - GFP_KERNEL); - - if (!gofh->bufs) { - mutex_unlock(&go->hw_lock); - goto unlock_and_return; - } - - for (i = 0; i < count; ++i) { - gofh->bufs[i].go = go; - gofh->bufs[i].index = i; - gofh->bufs[i].state = BUF_STATE_IDLE; - gofh->bufs[i].mapped = 0; - } - - go->in_use = 1; - } else { - go->in_use = 0; - } - - gofh->buf_count = count; - mutex_unlock(&go->hw_lock); - mutex_unlock(&gofh->lock); - - memset(req, 0, sizeof(*req)); - - req->count = count; - req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - req->memory = V4L2_MEMORY_MMAP; - - return 0; - -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; -} - -static int vidioc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct go7007_file *gofh = priv; - int retval = -EINVAL; - unsigned int index; - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return retval; - - index = buf->index; - - mutex_lock(&gofh->lock); - if (index >= gofh->buf_count) - goto unlock_and_return; - - memset(buf, 0, sizeof(*buf)); - buf->index = index; - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - switch (gofh->bufs[index].state) { - case BUF_STATE_QUEUED: - buf->flags = V4L2_BUF_FLAG_QUEUED; - break; - case BUF_STATE_DONE: - buf->flags = V4L2_BUF_FLAG_DONE; - break; - default: - buf->flags = 0; - } - - if (gofh->bufs[index].mapped) - buf->flags |= V4L2_BUF_FLAG_MAPPED; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = index * GO7007_BUF_SIZE; - buf->length = GO7007_BUF_SIZE; - mutex_unlock(&gofh->lock); - - return 0; - -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - struct go7007_buffer *gobuf; - unsigned long flags; - int retval = -EINVAL; - int ret; - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || - buf->memory != V4L2_MEMORY_MMAP) - return retval; - - mutex_lock(&gofh->lock); - if (buf->index < 0 || buf->index >= gofh->buf_count) - goto unlock_and_return; - - gobuf = &gofh->bufs[buf->index]; - if (!gobuf->mapped) - goto unlock_and_return; - - retval = -EBUSY; - if (gobuf->state != BUF_STATE_IDLE) - goto unlock_and_return; - - /* offset will be 0 until we really support USERPTR streaming */ - gobuf->offset = gobuf->user_addr & ~PAGE_MASK; - gobuf->bytesused = 0; - gobuf->frame_offset = 0; - gobuf->modet_active = 0; - if (gobuf->offset > 0) - gobuf->page_count = GO7007_BUF_PAGES + 1; - else - gobuf->page_count = GO7007_BUF_PAGES; - - retval = -ENOMEM; - down_read(¤t->mm->mmap_sem); - ret = get_user_pages(current, current->mm, - gobuf->user_addr & PAGE_MASK, gobuf->page_count, - 1, 1, gobuf->pages, NULL); - up_read(¤t->mm->mmap_sem); - - if (ret != gobuf->page_count) { - int i; - for (i = 0; i < ret; ++i) - page_cache_release(gobuf->pages[i]); - gobuf->page_count = 0; - goto unlock_and_return; - } - - gobuf->state = BUF_STATE_QUEUED; - spin_lock_irqsave(&go->spinlock, flags); - list_add_tail(&gobuf->stream, &go->stream); - spin_unlock_irqrestore(&go->spinlock, flags); - mutex_unlock(&gofh->lock); - - return 0; - -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; -} - - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - struct go7007_buffer *gobuf; - int retval = -EINVAL; - unsigned long flags; - u32 frame_type_flag; - DEFINE_WAIT(wait); - - if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return retval; - if (buf->memory != V4L2_MEMORY_MMAP) - return retval; - - mutex_lock(&gofh->lock); - if (list_empty(&go->stream)) - goto unlock_and_return; - gobuf = list_entry(go->stream.next, - struct go7007_buffer, stream); - - retval = -EAGAIN; - if (gobuf->state != BUF_STATE_DONE && - !(file->f_flags & O_NONBLOCK)) { - for (;;) { - prepare_to_wait(&go->frame_waitq, &wait, - TASK_INTERRUPTIBLE); - if (gobuf->state == BUF_STATE_DONE) - break; - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - } - finish_wait(&go->frame_waitq, &wait); - } - if (gobuf->state != BUF_STATE_DONE) - goto unlock_and_return; - - spin_lock_irqsave(&go->spinlock, flags); - deactivate_buffer(gobuf); - spin_unlock_irqrestore(&go->spinlock, flags); - frame_type_flag = get_frame_type_flag(gobuf, go->format); - gobuf->state = BUF_STATE_IDLE; - - memset(buf, 0, sizeof(*buf)); - buf->index = gobuf->index; - buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->bytesused = gobuf->bytesused; - buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag; - buf->field = V4L2_FIELD_NONE; - buf->timestamp = gobuf->timestamp; - buf->sequence = gobuf->seq; - buf->memory = V4L2_MEMORY_MMAP; - buf->m.offset = gobuf->index * GO7007_BUF_SIZE; - buf->length = GO7007_BUF_SIZE; - buf->reserved = gobuf->modet_active; - - mutex_unlock(&gofh->lock); - return 0; - -unlock_and_return: - mutex_unlock(&gofh->lock); - return retval; -} - -static int vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - int retval = 0; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - mutex_lock(&gofh->lock); - mutex_lock(&go->hw_lock); - - if (!go->streaming) { - go->streaming = 1; - go->next_seq = 0; - go->active_buf = NULL; - if (go7007_start_encoder(go) < 0) - retval = -EIO; - else - retval = 0; - } - mutex_unlock(&go->hw_lock); - mutex_unlock(&gofh->lock); - - return retval; -} - -static int vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct go7007_file *gofh = priv; - struct go7007 *go = gofh->go; - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mutex_lock(&gofh->lock); - go7007_streamoff(go); - mutex_unlock(&gofh->lock); - - return 0; -} - -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *query) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - int id = query->id; - - if (0 == call_all(&go->v4l2_dev, core, queryctrl, query)) - return 0; - - query->id = id; - return mpeg_query_ctrl(query); -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl)) - return 0; - - return mpeg_g_ctrl(ctrl, go); -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl)) - return 0; - - return mpeg_s_ctrl(ctrl, go); -} - -static int vidioc_g_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parm) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - struct v4l2_fract timeperframe = { - .numerator = 1001 * go->fps_scale, - .denominator = go->sensor_framerate, - }; - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; - parm->parm.capture.timeperframe = timeperframe; - - return 0; -} - -static int vidioc_s_parm(struct file *filp, void *priv, - struct v4l2_streamparm *parm) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - unsigned int n, d; - - if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (parm->parm.capture.capturemode != 0) - return -EINVAL; - - n = go->sensor_framerate * - parm->parm.capture.timeperframe.numerator; - d = 1001 * parm->parm.capture.timeperframe.denominator; - if (n != 0 && d != 0 && n > d) - go->fps_scale = (n + d/2) / d; - else - go->fps_scale = 1; - - return 0; -} - -/* VIDIOC_ENUMSTD on go7007 were used for enumberating the supported fps and - its resolution, when the device is not connected to TV. - This were an API abuse, probably used by the lack of specific IOCTL's to - enumberate it, by the time the driver were written. - - However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS - and VIDIOC_ENUM_FRAMESIZES) were added for this purpose. - - The two functions bellow implements the newer ioctls -*/ -static int vidioc_enum_framesizes(struct file *filp, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - /* Return -EINVAL, if it is a TV board */ - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || - (go->board_info->sensor_flags & GO7007_SENSOR_TV)) - return -EINVAL; - - if (fsize->index > 0) - return -EINVAL; - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - fsize->discrete.width = go->board_info->sensor_width; - fsize->discrete.height = go->board_info->sensor_height; - - return 0; -} - -static int vidioc_enum_frameintervals(struct file *filp, void *priv, - struct v4l2_frmivalenum *fival) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - /* Return -EINVAL, if it is a TV board */ - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || - (go->board_info->sensor_flags & GO7007_SENSOR_TV)) - return -EINVAL; - - if (fival->index > 0) - return -EINVAL; - - fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fival->discrete.numerator = 1001; - fival->discrete.denominator = go->board_info->sensor_framerate; - - return 0; -} - -static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - switch (go->standard) { - case GO7007_STD_NTSC: - *std = V4L2_STD_NTSC; - break; - case GO7007_STD_PAL: - *std = V4L2_STD_PAL; - break; - default: - return -EINVAL; - } - - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (go->streaming) - return -EBUSY; - - if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0) - return -EINVAL; - - if (*std == 0) - return -EINVAL; - - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - go->input == go->board_info->num_inputs - 1) { - if (!go->i2c_adapter_online) - return -EIO; - if (call_all(&go->v4l2_dev, core, s_std, *std) < 0) - return -EINVAL; - } - - if (*std & V4L2_STD_NTSC) { - go->standard = GO7007_STD_NTSC; - go->sensor_framerate = 30000; - } else if (*std & V4L2_STD_PAL) { - go->standard = GO7007_STD_PAL; - go->sensor_framerate = 25025; - } else if (*std & V4L2_STD_SECAM) { - go->standard = GO7007_STD_PAL; - go->sensor_framerate = 25025; - } else - return -EINVAL; - - call_all(&go->v4l2_dev, core, s_std, *std); - set_capture_size(go, NULL, 0); - - return 0; -} - -static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - go->input == go->board_info->num_inputs - 1) { - if (!go->i2c_adapter_online) - return -EIO; - return call_all(&go->v4l2_dev, video, querystd, std); - } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) - *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; - else - *std = 0; - - return 0; -} - -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (inp->index >= go->board_info->num_inputs) - return -EINVAL; - - strncpy(inp->name, go->board_info->inputs[inp->index].name, - sizeof(inp->name)); - - /* If this board has a tuner, it will be the last input */ - if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && - inp->index == go->board_info->num_inputs - 1) - inp->type = V4L2_INPUT_TYPE_TUNER; - else - inp->type = V4L2_INPUT_TYPE_CAMERA; - - inp->audioset = 0; - inp->tuner = 0; - if (go->board_info->sensor_flags & GO7007_SENSOR_TV) - inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | - V4L2_STD_SECAM; - else - inp->std = 0; - - return 0; -} - - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - *input = go->input; - - return 0; -} - -static int vidioc_s_input(struct file *file, void *priv, unsigned int input) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (input >= go->board_info->num_inputs) - return -EINVAL; - if (go->streaming) - return -EBUSY; - - go->input = input; - - return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0); -} - -static int vidioc_g_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (t->index != 0) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - - return call_all(&go->v4l2_dev, tuner, g_tuner, t); -} - -static int vidioc_s_tuner(struct file *file, void *priv, - struct v4l2_tuner *t) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (t->index != 0) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - - switch (go->board_id) { - case GO7007_BOARDID_PX_TV402U_NA: - case GO7007_BOARDID_PX_TV402U_JP: - /* No selectable options currently */ - if (t->audmode != V4L2_TUNER_MODE_STEREO) - return -EINVAL; - break; - } - - return call_all(&go->v4l2_dev, tuner, s_tuner, t); -} - -static int vidioc_g_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - - f->type = V4L2_TUNER_ANALOG_TV; - - return call_all(&go->v4l2_dev, tuner, g_frequency, f); -} - -static int vidioc_s_frequency(struct file *file, void *priv, - struct v4l2_frequency *f) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) - return -EINVAL; - if (!go->i2c_adapter_online) - return -EIO; - - return call_all(&go->v4l2_dev, tuner, s_frequency, f); -} - -static int vidioc_cropcap(struct file *file, void *priv, - struct v4l2_cropcap *cropcap) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - /* These specify the raw input of the sensor */ - switch (go->standard) { - case GO7007_STD_NTSC: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = 480; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = 720; - cropcap->defrect.height = 480; - break; - case GO7007_STD_PAL: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = 720; - cropcap->bounds.height = 576; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = 720; - cropcap->defrect.height = 576; - break; - case GO7007_STD_OTHER: - cropcap->bounds.top = 0; - cropcap->bounds.left = 0; - cropcap->bounds.width = go->board_info->sensor_width; - cropcap->bounds.height = go->board_info->sensor_height; - cropcap->defrect.top = 0; - cropcap->defrect.left = 0; - cropcap->defrect.width = go->board_info->sensor_width; - cropcap->defrect.height = go->board_info->sensor_height; - break; - } - - return 0; -} - -static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) -{ - struct go7007 *go = ((struct go7007_file *) priv)->go; - - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - - /* These specify the raw input of the sensor */ - switch (go->standard) { - case GO7007_STD_NTSC: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = 720; - crop->c.height = 480; - break; - case GO7007_STD_PAL: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = 720; - crop->c.height = 576; - break; - case GO7007_STD_OTHER: - crop->c.top = 0; - crop->c.left = 0; - crop->c.width = go->board_info->sensor_width; - crop->c.height = go->board_info->sensor_height; - break; - } - - return 0; -} - -/* FIXME: vidioc_s_crop is not really implemented!!! - */ -static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) -{ - if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return 0; -} - -static int vidioc_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *params) -{ - memset(params, 0, sizeof(*params)); - params->quality = 50; /* ?? */ - params->jpeg_markers = V4L2_JPEG_MARKER_DHT | - V4L2_JPEG_MARKER_DQT; - - return 0; -} - -static int vidioc_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *params) -{ - if (params->quality != 50 || - params->jpeg_markers != (V4L2_JPEG_MARKER_DHT | - V4L2_JPEG_MARKER_DQT)) - return -EINVAL; - - return 0; -} - -/* FIXME: - Those ioctls are private, and not needed, since several standard - extended controls already provide streaming control. - So, those ioctls should be converted into vidioc_g_ext_ctrls() - and vidioc_s_ext_ctrls() - */ - -#if 0 - /* Temporary ioctls for controlling compression characteristics */ - case GO7007IOC_S_BITRATE: - { - int *bitrate = arg; - - if (go->streaming) - return -EINVAL; - /* Upper bound is kind of arbitrary here */ - if (*bitrate < 64000 || *bitrate > 10000000) - return -EINVAL; - go->bitrate = *bitrate; - return 0; - } - case GO7007IOC_G_BITRATE: - { - int *bitrate = arg; - - *bitrate = go->bitrate; - return 0; - } - case GO7007IOC_S_COMP_PARAMS: - { - struct go7007_comp_params *comp = arg; - - if (go->format == GO7007_FORMAT_MJPEG) - return -EINVAL; - if (comp->gop_size > 0) - go->gop_size = comp->gop_size; - else - go->gop_size = go->sensor_framerate / 1000; - if (go->gop_size != 15) - go->dvd_mode = 0; - /*go->ipb = comp->max_b_frames > 0;*/ /* completely untested */ - if (go->board_info->sensor_flags & GO7007_SENSOR_TV) { - switch (comp->aspect_ratio) { - case GO7007_ASPECT_RATIO_4_3_NTSC: - case GO7007_ASPECT_RATIO_4_3_PAL: - go->aspect_ratio = GO7007_RATIO_4_3; - break; - case GO7007_ASPECT_RATIO_16_9_NTSC: - case GO7007_ASPECT_RATIO_16_9_PAL: - go->aspect_ratio = GO7007_RATIO_16_9; - break; - default: - go->aspect_ratio = GO7007_RATIO_1_1; - break; - } - } - if (comp->flags & GO7007_COMP_OMIT_SEQ_HEADER) { - go->dvd_mode = 0; - go->seq_header_enable = 0; - } else { - go->seq_header_enable = 1; - } - /* fall-through */ - } - case GO7007IOC_G_COMP_PARAMS: - { - struct go7007_comp_params *comp = arg; - - if (go->format == GO7007_FORMAT_MJPEG) - return -EINVAL; - memset(comp, 0, sizeof(*comp)); - comp->gop_size = go->gop_size; - comp->max_b_frames = go->ipb ? 2 : 0; - switch (go->aspect_ratio) { - case GO7007_RATIO_4_3: - if (go->standard == GO7007_STD_NTSC) - comp->aspect_ratio = - GO7007_ASPECT_RATIO_4_3_NTSC; - else - comp->aspect_ratio = - GO7007_ASPECT_RATIO_4_3_PAL; - break; - case GO7007_RATIO_16_9: - if (go->standard == GO7007_STD_NTSC) - comp->aspect_ratio = - GO7007_ASPECT_RATIO_16_9_NTSC; - else - comp->aspect_ratio = - GO7007_ASPECT_RATIO_16_9_PAL; - break; - default: - comp->aspect_ratio = GO7007_ASPECT_RATIO_1_1; - break; - } - if (go->closed_gop) - comp->flags |= GO7007_COMP_CLOSED_GOP; - if (!go->seq_header_enable) - comp->flags |= GO7007_COMP_OMIT_SEQ_HEADER; - return 0; - } - case GO7007IOC_S_MPEG_PARAMS: - { - struct go7007_mpeg_params *mpeg = arg; - - if (go->format != GO7007_FORMAT_MPEG1 && - go->format != GO7007_FORMAT_MPEG2 && - go->format != GO7007_FORMAT_MPEG4) - return -EINVAL; - - if (mpeg->flags & GO7007_MPEG_FORCE_DVD_MODE) { - go->format = GO7007_FORMAT_MPEG2; - go->bitrate = 9800000; - go->gop_size = 15; - go->pali = 0x48; - go->closed_gop = 1; - go->repeat_seqhead = 0; - go->seq_header_enable = 1; - go->gop_header_enable = 1; - go->dvd_mode = 1; - } else { - switch (mpeg->mpeg_video_standard) { - case GO7007_MPEG_VIDEO_MPEG1: - go->format = GO7007_FORMAT_MPEG1; - go->pali = 0; - break; - case GO7007_MPEG_VIDEO_MPEG2: - go->format = GO7007_FORMAT_MPEG2; - if (mpeg->pali >> 24 == 2) - go->pali = mpeg->pali & 0xff; - else - go->pali = 0x48; - break; - case GO7007_MPEG_VIDEO_MPEG4: - go->format = GO7007_FORMAT_MPEG4; - if (mpeg->pali >> 24 == 4) - go->pali = mpeg->pali & 0xff; - else - go->pali = 0xf5; - break; - default: - return -EINVAL; - } - go->gop_header_enable = - mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER - ? 0 : 1; - if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER) - go->repeat_seqhead = 1; - else - go->repeat_seqhead = 0; - go->dvd_mode = 0; - } - /* fall-through */ - } - case GO7007IOC_G_MPEG_PARAMS: - { - struct go7007_mpeg_params *mpeg = arg; - - memset(mpeg, 0, sizeof(*mpeg)); - switch (go->format) { - case GO7007_FORMAT_MPEG1: - mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG1; - mpeg->pali = 0; - break; - case GO7007_FORMAT_MPEG2: - mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG2; - mpeg->pali = GO7007_MPEG_PROFILE(2, go->pali); - break; - case GO7007_FORMAT_MPEG4: - mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG4; - mpeg->pali = GO7007_MPEG_PROFILE(4, go->pali); - break; - default: - return -EINVAL; - } - if (!go->gop_header_enable) - mpeg->flags |= GO7007_MPEG_OMIT_GOP_HEADER; - if (go->repeat_seqhead) - mpeg->flags |= GO7007_MPEG_REPEAT_SEQHEADER; - if (go->dvd_mode) - mpeg->flags |= GO7007_MPEG_FORCE_DVD_MODE; - return 0; - } - case GO7007IOC_S_MD_PARAMS: - { - struct go7007_md_params *mdp = arg; - - if (mdp->region > 3) - return -EINVAL; - if (mdp->trigger > 0) { - go->modet[mdp->region].pixel_threshold = - mdp->pixel_threshold >> 1; - go->modet[mdp->region].motion_threshold = - mdp->motion_threshold >> 1; - go->modet[mdp->region].mb_threshold = - mdp->trigger >> 1; - go->modet[mdp->region].enable = 1; - } else - go->modet[mdp->region].enable = 0; - /* fall-through */ - } - case GO7007IOC_G_MD_PARAMS: - { - struct go7007_md_params *mdp = arg; - int region = mdp->region; - - if (mdp->region > 3) - return -EINVAL; - memset(mdp, 0, sizeof(struct go7007_md_params)); - mdp->region = region; - if (!go->modet[region].enable) - return 0; - mdp->pixel_threshold = - (go->modet[region].pixel_threshold << 1) + 1; - mdp->motion_threshold = - (go->modet[region].motion_threshold << 1) + 1; - mdp->trigger = - (go->modet[region].mb_threshold << 1) + 1; - return 0; - } - case GO7007IOC_S_MD_REGION: - { - struct go7007_md_region *region = arg; - - if (region->region < 1 || region->region > 3) - return -EINVAL; - return clip_to_modet_map(go, region->region, region->clips); - } -#endif - -static ssize_t go7007_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -static void go7007_vm_open(struct vm_area_struct *vma) -{ - struct go7007_buffer *gobuf = vma->vm_private_data; - - ++gobuf->mapped; -} - -static void go7007_vm_close(struct vm_area_struct *vma) -{ - struct go7007_buffer *gobuf = vma->vm_private_data; - unsigned long flags; - - if (--gobuf->mapped == 0) { - spin_lock_irqsave(&gobuf->go->spinlock, flags); - deactivate_buffer(gobuf); - spin_unlock_irqrestore(&gobuf->go->spinlock, flags); - } -} - -/* Copied from videobuf-dma-sg.c */ -static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) -{ - struct page *page; - - page = alloc_page(GFP_USER | __GFP_DMA32); - if (!page) - return VM_FAULT_OOM; - clear_user_highpage(page, (unsigned long)vmf->virtual_address); - vmf->page = page; - return 0; -} - -static struct vm_operations_struct go7007_vm_ops = { - .open = go7007_vm_open, - .close = go7007_vm_close, - .fault = go7007_vm_fault, -}; - -static int go7007_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct go7007_file *gofh = file->private_data; - unsigned int index; - - if (gofh->go->status != STATUS_ONLINE) - return -EIO; - if (!(vma->vm_flags & VM_SHARED)) - return -EINVAL; /* only support VM_SHARED mapping */ - if (vma->vm_end - vma->vm_start != GO7007_BUF_SIZE) - return -EINVAL; /* must map exactly one full buffer */ - mutex_lock(&gofh->lock); - index = vma->vm_pgoff / GO7007_BUF_PAGES; - if (index >= gofh->buf_count) { - mutex_unlock(&gofh->lock); - return -EINVAL; /* trying to map beyond requested buffers */ - } - if (index * GO7007_BUF_PAGES != vma->vm_pgoff) { - mutex_unlock(&gofh->lock); - return -EINVAL; /* offset is not aligned on buffer boundary */ - } - if (gofh->bufs[index].mapped > 0) { - mutex_unlock(&gofh->lock); - return -EBUSY; - } - gofh->bufs[index].mapped = 1; - gofh->bufs[index].user_addr = vma->vm_start; - vma->vm_ops = &go7007_vm_ops; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_flags &= ~VM_IO; - vma->vm_private_data = &gofh->bufs[index]; - mutex_unlock(&gofh->lock); - return 0; -} - -static unsigned int go7007_poll(struct file *file, poll_table *wait) -{ - struct go7007_file *gofh = file->private_data; - struct go7007_buffer *gobuf; - - if (list_empty(&gofh->go->stream)) - return POLLERR; - gobuf = list_entry(gofh->go->stream.next, struct go7007_buffer, stream); - poll_wait(file, &gofh->go->frame_waitq, wait); - if (gobuf->state == BUF_STATE_DONE) - return POLLIN | POLLRDNORM; - return 0; -} - -static void go7007_vfl_release(struct video_device *vfd) -{ - struct go7007 *go = video_get_drvdata(vfd); - - video_device_release(vfd); - if (--go->ref_count == 0) - kfree(go); -} - -static struct v4l2_file_operations go7007_fops = { - .owner = THIS_MODULE, - .open = go7007_open, - .release = go7007_release, - .ioctl = video_ioctl2, - .read = go7007_read, - .mmap = go7007_mmap, - .poll = go7007_poll, -}; - -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_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_g_std = vidioc_g_std, - .vidioc_s_std = vidioc_s_std, - .vidioc_querystd = vidioc_querystd, - .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, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_s_parm = vidioc_s_parm, - .vidioc_enum_framesizes = vidioc_enum_framesizes, - .vidioc_enum_frameintervals = vidioc_enum_frameintervals, - .vidioc_cropcap = vidioc_cropcap, - .vidioc_g_crop = vidioc_g_crop, - .vidioc_s_crop = vidioc_s_crop, - .vidioc_g_jpegcomp = vidioc_g_jpegcomp, - .vidioc_s_jpegcomp = vidioc_s_jpegcomp, -}; - -static struct video_device go7007_template = { - .name = "go7007", - .fops = &go7007_fops, - .release = go7007_vfl_release, - .ioctl_ops = &video_ioctl_ops, - .tvnorms = V4L2_STD_ALL, - .current_norm = V4L2_STD_NTSC, -}; - -int go7007_v4l2_init(struct go7007 *go) -{ - int rv; - - go->video_dev = video_device_alloc(); - if (go->video_dev == NULL) - return -ENOMEM; - *go->video_dev = go7007_template; - go->video_dev->parent = go->dev; - rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1); - if (rv < 0) { - video_device_release(go->video_dev); - go->video_dev = NULL; - return rv; - } - rv = v4l2_device_register(go->dev, &go->v4l2_dev); - if (rv < 0) { - video_device_release(go->video_dev); - go->video_dev = NULL; - return rv; - } - video_set_drvdata(go->video_dev, go); - ++go->ref_count; - printk(KERN_INFO "%s: registered device %s [v4l2]\n", - go->video_dev->name, video_device_node_name(go->video_dev)); - - return 0; -} - -void go7007_v4l2_remove(struct go7007 *go) -{ - unsigned long flags; - - mutex_lock(&go->hw_lock); - if (go->streaming) { - go->streaming = 0; - go7007_stream_stop(go); - spin_lock_irqsave(&go->spinlock, flags); - abort_queued(go); - spin_unlock_irqrestore(&go->spinlock, flags); - } - mutex_unlock(&go->hw_lock); - if (go->video_dev) - video_unregister_device(go->video_dev); - v4l2_device_unregister(&go->v4l2_dev); -} diff --git a/drivers/staging/go7007/go7007.h b/drivers/staging/go7007/go7007.h deleted file mode 100644 index 7399c915a934..000000000000 --- a/drivers/staging/go7007/go7007.h +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and the associated README documentation file (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -/* DEPRECATED -- use V4L2_PIX_FMT_MPEG and then call GO7007IOC_S_MPEG_PARAMS - * to select between MPEG1, MPEG2, and MPEG4 */ -#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG4 */ - -/* These will be replaced with a better interface - * soon, so don't get too attached to them */ -#define GO7007IOC_S_BITRATE _IOW('V', BASE_VIDIOC_PRIVATE + 0, int) -#define GO7007IOC_G_BITRATE _IOR('V', BASE_VIDIOC_PRIVATE + 1, int) - -enum go7007_aspect_ratio { - GO7007_ASPECT_RATIO_1_1 = 0, - GO7007_ASPECT_RATIO_4_3_NTSC = 1, - GO7007_ASPECT_RATIO_4_3_PAL = 2, - GO7007_ASPECT_RATIO_16_9_NTSC = 3, - GO7007_ASPECT_RATIO_16_9_PAL = 4, -}; - -/* Used to set generic compression parameters */ -struct go7007_comp_params { - __u32 gop_size; - __u32 max_b_frames; - enum go7007_aspect_ratio aspect_ratio; - __u32 flags; - __u32 reserved[8]; -}; - -#define GO7007_COMP_CLOSED_GOP 0x00000001 -#define GO7007_COMP_OMIT_SEQ_HEADER 0x00000002 - -enum go7007_mpeg_video_standard { - GO7007_MPEG_VIDEO_MPEG1 = 0, - GO7007_MPEG_VIDEO_MPEG2 = 1, - GO7007_MPEG_VIDEO_MPEG4 = 2, -}; - -/* Used to set parameters for V4L2_PIX_FMT_MPEG format */ -struct go7007_mpeg_params { - enum go7007_mpeg_video_standard mpeg_video_standard; - __u32 flags; - __u32 pali; - __u32 reserved[8]; -}; - -#define GO7007_MPEG_FORCE_DVD_MODE 0x00000001 -#define GO7007_MPEG_OMIT_GOP_HEADER 0x00000002 -#define GO7007_MPEG_REPEAT_SEQHEADER 0x00000004 - -#define GO7007_MPEG_PROFILE(format, pali) (((format)<<24)|(pali)) - -#define GO7007_MPEG2_PROFILE_MAIN_MAIN GO7007_MPEG_PROFILE(2, 0x48) - -#define GO7007_MPEG4_PROFILE_S_L0 GO7007_MPEG_PROFILE(4, 0x08) -#define GO7007_MPEG4_PROFILE_S_L1 GO7007_MPEG_PROFILE(4, 0x01) -#define GO7007_MPEG4_PROFILE_S_L2 GO7007_MPEG_PROFILE(4, 0x02) -#define GO7007_MPEG4_PROFILE_S_L3 GO7007_MPEG_PROFILE(4, 0x03) -#define GO7007_MPEG4_PROFILE_ARTS_L1 GO7007_MPEG_PROFILE(4, 0x91) -#define GO7007_MPEG4_PROFILE_ARTS_L2 GO7007_MPEG_PROFILE(4, 0x92) -#define GO7007_MPEG4_PROFILE_ARTS_L3 GO7007_MPEG_PROFILE(4, 0x93) -#define GO7007_MPEG4_PROFILE_ARTS_L4 GO7007_MPEG_PROFILE(4, 0x94) -#define GO7007_MPEG4_PROFILE_AS_L0 GO7007_MPEG_PROFILE(4, 0xf0) -#define GO7007_MPEG4_PROFILE_AS_L1 GO7007_MPEG_PROFILE(4, 0xf1) -#define GO7007_MPEG4_PROFILE_AS_L2 GO7007_MPEG_PROFILE(4, 0xf2) -#define GO7007_MPEG4_PROFILE_AS_L3 GO7007_MPEG_PROFILE(4, 0xf3) -#define GO7007_MPEG4_PROFILE_AS_L4 GO7007_MPEG_PROFILE(4, 0xf4) -#define GO7007_MPEG4_PROFILE_AS_L5 GO7007_MPEG_PROFILE(4, 0xf5) - -struct go7007_md_params { - __u16 region; - __u16 trigger; - __u16 pixel_threshold; - __u16 motion_threshold; - __u32 reserved[8]; -}; - -struct go7007_md_region { - __u16 region; - __u16 flags; - struct v4l2_clip *clips; - __u32 reserved[8]; -}; - -#define GO7007IOC_S_MPEG_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 2, \ - struct go7007_mpeg_params) -#define GO7007IOC_G_MPEG_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 3, \ - struct go7007_mpeg_params) -#define GO7007IOC_S_COMP_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 4, \ - struct go7007_comp_params) -#define GO7007IOC_G_COMP_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 5, \ - struct go7007_comp_params) -#define GO7007IOC_S_MD_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 6, \ - struct go7007_md_params) -#define GO7007IOC_G_MD_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 7, \ - struct go7007_md_params) -#define GO7007IOC_S_MD_REGION _IOW('V', BASE_VIDIOC_PRIVATE + 8, \ - struct go7007_md_region) diff --git a/drivers/staging/go7007/go7007.txt b/drivers/staging/go7007/go7007.txt deleted file mode 100644 index 9db1f3952fd2..000000000000 --- a/drivers/staging/go7007/go7007.txt +++ /dev/null @@ -1,481 +0,0 @@ -This is a driver for the WIS GO7007SB multi-format video encoder. - -Pete Eberlein - -The driver was originally released under the GPL and is currently hosted at: -http://nikosapi.org/wiki/index.php/WIS_Go7007_Linux_driver -The go7007 firmware can be acquired from the package on the site above. - -I've modified the driver to support the following Video4Linux2 MPEG -controls, with acceptable values: - -V4L2_CID_MPEG_STREAM_TYPE V4L2_MPEG_STREAM_TYPE_MPEG2_DVD - V4L2_MPEG_STREAM_TYPE_MPEG_ELEM -V4L2_CID_MPEG_VIDEO_ENCODING V4L2_MPEG_VIDEO_ENCODING_MPEG_1 - V4L2_MPEG_VIDEO_ENCODING_MPEG_2 - V4L2_MPEG_VIDEO_ENCODING_MPEG_4 -V4L2_CID_MPEG_VIDEO_ASPECT V4L2_MPEG_VIDEO_ASPECT_1x1 - V4L2_MPEG_VIDEO_ASPECT_4x3 - V4L2_MPEG_VIDEO_ASPECT_16x9 -V4L2_CID_MPEG_VIDEO_GOP_SIZE integer -V4L2_CID_MPEG_VIDEO_BITRATE 64000 .. 10000000 - -These should be used instead of the non-standard GO7007 ioctls described -below. - - -The README files from the orignal package appear below: - ---------------------------------------------------------------------------- - WIS GO7007SB Public Linux Driver ---------------------------------------------------------------------------- - - -*** Please see the file RELEASE-NOTES for important last-minute updates *** - - - 0. OVERVIEW AND LICENSING/DISCLAIMER - - -This driver kit contains Linux drivers for the WIS GO7007SB multi-format -video encoder. Only kernel version 2.6.x is supported. The video stream -is available through the Video4Linux2 API and the audio stream is available -through the ALSA API (or the OSS emulation layer of the ALSA system). - -The files in kernel/ and hotplug/ are licensed under the GNU General Public -License Version 2 from the Free Software Foundation. A copy of the license -is included in the file COPYING. - -The example applications in apps/ and C header files in include/ are -licensed under a permissive license included in the source files which -allows copying, modification and redistribution for any purpose without -attribution. - -The firmware files included in the firmware/ directory may be freely -redistributed only in conjunction with this document; but modification, -tampering and reverse engineering are prohibited. - -MICRONAS USA, INC., MAKES NO WARRANTIES TO ANY PERSON OR ENTITY WITH -RESPECT TO THE SOFTWARE OR ANY DERIVATIVES THEREOF OR ANY SERVICES OR -LICENSES AND DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION -WARRANTIES OF MERCHANTABILITY, SUPPORT, AND FITNESS FOR A PARTICULAR -PURPOSE AND NON-INFRINGEMENT. - - - 1. SYSTEM REQUIREMENTS - - -This driver requires Linux kernel 2.6. Kernel 2.4 is not supported. Using -kernel 2.6.10 or later is recommended, as earlier kernels are known to have -unstable USB 2.0 support. - -A fully built kernel source tree must be available. Typically this will be -linked from "/lib/modules//build" for convenience. If this -link does not exist, an extra parameter will need to be passed to the -`make` command. - -All vendor-built kernels should already be configured properly. However, -for custom-built kernels, the following options need to be enabled in the -kernel as built-in or modules: - - CONFIG_HOTPLUG - Support for hot-pluggable devices - CONFIG_MODULES - Enable loadable module support - CONFIG_KMOD - Automatic kernel module loading - CONFIG_FW_LOADER - Hotplug firmware loading support - CONFIG_I2C - I2C support - CONFIG_VIDEO_DEV - Video For Linux - CONFIG_SOUND - Sound card support - CONFIG_SND - Advanced Linux Sound Architecture - CONFIG_USB - Support for Host-side USB - CONFIG_USB_DEVICEFS - USB device filesystem - CONFIG_USB_EHCI_HCD - EHCI HCD (USB 2.0) support - -Additionally, to use the example application, the following options need to -be enabled in the ALSA section: - - CONFIG_SND_MIXER_OSS - OSS Mixer API - CONFIG_SND_PCM_OSS - OSS PCM (digital audio) API - -The hotplug scripts, along with the fxload utility, must also be installed. -These scripts can be obtained from . -Hotplugging is used for loading firmware into the Cypruss EZ-USB chip using -fxload and for loading firmware into the driver using the firmware agent. - - - 2. COMPILING AND INSTALLING THE DRIVER - - -Most users should be able to compile the driver by simply running: - - $ make - -in the top-level directory of the driver kit. First the kernel modules -will be built, followed by the example applications. - -If the build system is unable to locate the kernel source tree for the -currently-running kernel, or if the module should be built for a kernel -other than the currently-running kernel, an additional parameter will need -to be passed to make to specify the appropriate kernel source directory: - - $ make KERNELSRC=/usr/src/linux-2.6.10-custom3 - -Once the compile completes, the driver and firmware files should be -installed by running: - - $ make install - -The kernel modules will be placed in "/lib/modules//extra" -and the firmware files will be placed in the appropriate hotplug firmware -directory, usually /lib/firmware. In addition, USB maps and scripts will -be placed in /etc/hotplug/usb to enable fxload to initialize the EZ-USB -control chip when the device is connected. - - - 3. PAL/SECAM TUNER CONFIGURATION (TV402U-EU only) - - -The PAL model of the Plextor ConvertX TV402U may require additional -configuration to correctly select the appropriate TV frequency band and -audio subchannel. - -Users with a device other than the Plextor ConvertX TV402U-EU should skip -this section. - -The wide variety of PAL TV systems used in Europe requires that additional -information about the local TV standards be passed to the driver in order -to properly tune TV channels. The two necessary parameters are (a) the PAL -TV band, and (b) the audio subchannel format in use. - -In many cases, the appropriate TV band selection is passed to the driver -from applications. However, in some cases, the application only specifies -that the driver should use PAL but not the specific information about the -appropriate TV band. To work around this issue, the correct TV band may be -specified in the "force_band" parameter to the wis-sony-tuner module: - - TV band force_band - ------- ---------- - PAL B/G B - PAL I I - PAL D/K D - SECAM L L - -If the "force_band" parameter is specified, the driver will ignore any TV -band specified by applications and will always use the band provided in the -module parameter. - -The other parameter that can be specified is the audio subchannel format. -There are several stereo audio carrier systems in use, including NICAM and -three varieties of A2. To receive audio broadcast on one of these stereo -carriers, the "force_mpx_mode" parameter must be specified to the -wis-sony-tuner module. - - TV band Audio subcarrier force_mpx_mode - ------- ---------------- -------------- - PAL B/G Mono (default) 1 - PAL B/G A2 2 - PAL B/G NICAM 3 - PAL I Mono (default) 4 - PAL I NICAM 5 - PAL D/K Mono (default) 6 - PAL D/K A2 (1) 7 - PAL D/K A2 (2) 8 - PAL D/K A2 (3) 9 - PAL D/K NICAM 10 - SECAM L Mono (default) 11 - SECAM L NICAM 12 - -If the "force_mpx_mode" parameter is not specified, the correct mono-only -mode will be chosen based on the TV band. However, the tuner will not -receive stereo audio or bilingual broadcasts correctly. - -To pass the "force_band" or "force_mpx_mode" parameters to the -wis-sony-tuner module, the following line must be added to the modprobe -configuration file, which varies from one Linux distribution to another. - - options wis-sony-tuner force_band=B force_mpx_mode=2 - -The above example would force the tuner to the PAL B/G TV band and receive -stereo audio broadcasts on the A2 carrier. - -To verify that the configuration has been placed in the correct location, -execute: - - $ modprobe -c | grep wis-sony-tuner - -If the configuration line appears, then modprobe will pass the parameters -correctly the next time the wis-sony-tuner module is loaded into the -kernel. - - - 4. TESTING THE DRIVER - - -Because few Linux applications are able to correctly capture from -Video4Linux2 devices with only compressed formats supported, the new driver -should be tested with the "gorecord" application in the apps/ directory. - -First connect a video source to the device, such as a DVD player or VCR. -This will be captured to a file for testing the driver. If an input source -is unavailable, a test file can still be captured, but the video will be -black and the audio will be silent. - -This application will auto-detect the V4L2 and ALSA/OSS device names of the -hardware and will record video and audio to an AVI file for a specified -number of seconds. For example: - - $ apps/gorecord -duration 60 capture.avi - -If this application does not successfully record an AVI file, the error -messages produced by gorecord and recorded in the system log (usually in -/var/log/messages) should provide information to help resolve the problem. - -Supplying no parameters to gorecord will cause it to probe the available -devices and exit. Use the -help flag for usage information. - - - 5. USING THE DRIVER - - -The V4L2 device implemented by the driver provides a standard compressed -format API, within the following criteria: - - * Applications that only support the original Video4Linux1 API will not - be able to communicate with this driver at all. - - * No raw video modes are supported, so applications like xawtv that - expect only uncompressed video will not function. - - * Supported compression formats are: Motion-JPEG, MPEG1, MPEG2 and MPEG4. - - * MPEG video formats are delivered as Video Elementary Streams only. - Program Stream (PS), Transport Stream (TS) and Packetized Elementary - Stream (PES) formats are not supported. - - * Video parameters such as format and input port may not be changed while - the encoder is active. - - * The audio capture device only functions when the video encoder is - actively capturing video. Attempts to read from the audio device when - the encoder is inactive will result in an I/O error. - - * The native format of the audio device is 48Khz 2-channel 16-bit - little-endian PCM, delivered through the ALSA system. No audio - compression is implemented in the hardware. ALSA may convert to other - uncompressed formats on the fly. - -The include/ directory contains a C header file describing non-standard -features of the GO7007SB encoder, which are described below: - - - GO7007IOC_S_COMP_PARAMS, GO7007IOC_G_COMP_PARAMS - - These ioctls are used to negotiate general compression parameters. - - To query the current parameters, call the GO7007IOC_G_COMP_PARAMS ioctl - with a pointer to a struct go7007_comp_params. If the driver is not - set to MPEG format, the EINVAL error code will be returned. - - To change the current parameters, initialize all fields of a struct - go7007_comp_params and call the GO7007_IOC_S_COMP_PARAMS ioctl with a - pointer to this structure. The driver will return the current - parameters with any necessary changes to conform to the limitations of - the hardware or current compression mode. Any or all fields can be set - to zero to request a reasonable default value. If the driver is not - set to MPEG format, the EINVAL error code will be returned. When I/O - is in progress, the EBUSY error code will be returned. - - Fields in struct go7007_comp_params: - - __u32 The maximum number of frames in each - gop_size Group Of Pictures; i.e. the maximum - number of frames minus one between - each key frame. - - __u32 The maximum number of sequential - max_b_frames bidirectionally-predicted frames. - (B-frames are not yet supported.) - - enum go7007_aspect_ratio The aspect ratio to be encoded in the - aspect_ratio meta-data of the compressed format. - - Choices are: - GO7007_ASPECT_RATIO_1_1 - GO7007_ASPECT_RATIO_4_3_NTSC - GO7007_ASPECT_RATIO_4_3_PAL - GO7007_ASPECT_RATIO_16_9_NTSC - GO7007_ASPECT_RATIO_16_9_PAL - - __u32 Bit-wise OR of control flags (below) - flags - - Flags in struct go7007_comp_params: - - GO7007_COMP_CLOSED_GOP Only produce self-contained GOPs, used - to produce streams appropriate for - random seeking. - - GO7007_COMP_OMIT_SEQ_HEADER Omit the stream sequence header. - - - GO7007IOC_S_MPEG_PARAMS, GO7007IOC_G_MPEG_PARAMS - - These ioctls are used to negotiate MPEG-specific stream parameters when - the pixelformat has been set to V4L2_PIX_FMT_MPEG. - - To query the current parameters, call the GO7007IOC_G_MPEG_PARAMS ioctl - with a pointer to a struct go7007_mpeg_params. If the driver is not - set to MPEG format, the EINVAL error code will be returned. - - To change the current parameters, initialize all fields of a struct - go7007_mpeg_params and call the GO7007_IOC_S_MPEG_PARAMS ioctl with a - pointer to this structure. The driver will return the current - parameters with any necessary changes to conform to the limitations of - the hardware or selected MPEG mode. Any or all fields can be set to - zero to request a reasonable default value. If the driver is not set - to MPEG format, the EINVAL error code will be returned. When I/O is in - progress, the EBUSY error code will be returned. - - Fields in struct go7007_mpeg_params: - - enum go7007_mpeg_video_standard - mpeg_video_standard The MPEG video standard in which to - compress the video. - - Choices are: - GO7007_MPEG_VIDEO_MPEG1 - GO7007_MPEG_VIDEO_MPEG2 - GO7007_MPEG_VIDEO_MPEG4 - - __u32 Bit-wise OR of control flags (below) - flags - - __u32 The profile and level indication to be - pali stored in the sequence header. This - is only used as an indicator to the - decoder, and does not affect the MPEG - features used in the video stream. - Not valid for MPEG1. - - Choices for MPEG2 are: - GO7007_MPEG2_PROFILE_MAIN_MAIN - - Choices for MPEG4 are: - GO7007_MPEG4_PROFILE_S_L0 - GO7007_MPEG4_PROFILE_S_L1 - GO7007_MPEG4_PROFILE_S_L2 - GO7007_MPEG4_PROFILE_S_L3 - GO7007_MPEG4_PROFILE_ARTS_L1 - GO7007_MPEG4_PROFILE_ARTS_L2 - GO7007_MPEG4_PROFILE_ARTS_L3 - GO7007_MPEG4_PROFILE_ARTS_L4 - GO7007_MPEG4_PROFILE_AS_L0 - GO7007_MPEG4_PROFILE_AS_L1 - GO7007_MPEG4_PROFILE_AS_L2 - GO7007_MPEG4_PROFILE_AS_L3 - GO7007_MPEG4_PROFILE_AS_L4 - GO7007_MPEG4_PROFILE_AS_L5 - - Flags in struct go7007_mpeg_params: - - GO7007_MPEG_FORCE_DVD_MODE Force all compression parameters and - bitrate control settings to comply - with DVD MPEG2 stream requirements. - This overrides most compression and - bitrate settings! - - GO7007_MPEG_OMIT_GOP_HEADER Omit the GOP header. - - GO7007_MPEG_REPEAT_SEQHEADER Repeat the MPEG sequence header at - the start of each GOP. - - - GO7007IOC_S_BITRATE, GO7007IOC_G_BITRATE - - These ioctls are used to set and query the target bitrate value for the - compressed video stream. The bitrate may be selected by storing the - target bits per second in an int and calling GO7007IOC_S_BITRATE with a - pointer to the int. The bitrate may be queried by calling - GO7007IOC_G_BITRATE with a pointer to an int where the current bitrate - will be stored. - - Note that this is the primary means of controlling the video quality - for all compression modes, including V4L2_PIX_FMT_MJPEG. The - VIDIOC_S_JPEGCOMP ioctl is not supported. - - ----------------------------------------------------------------------------- - Installing the WIS PCI Voyager Driver ---------------------------------------------------------------------------- - -The WIS PCI Voyager driver requires several patches to the Linux 2.6.11.x -kernel source tree before compiling the driver. These patches update the -in-kernel SAA7134 driver to the newest development version and patch bugs -in the TDA8290/TDA8275 tuner driver. - -The following patches must be downloaded from Gerd Knorr's website and -applied in the order listed: - - http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner - http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner2 - http://dl.bytesex.org/patches/2.6.11-2/v4l2-api-mpeg - http://dl.bytesex.org/patches/2.6.11-2/saa7134-update - -The following patches are included with this SDK and can be applied in any -order: - - patches/2.6.11/saa7134-voyager.diff - patches/2.6.11/tda8275-newaddr.diff - patches/2.6.11/tda8290-ntsc.diff - -Check to make sure the CONFIG_VIDEO_SAA7134 option is enabled in the kernel -configuration, and build and install the kernel. - -After rebooting into the new kernel, the GO7007 driver can be compiled and -installed: - - $ make SAA7134_BUILD=y - $ make install - $ modprobe saa7134-go7007 - -There will be two V4L video devices associated with the PCI Voyager. The -first device (most likely /dev/video0) provides access to the raw video -capture mode of the SAA7133 device and is used to configure the source -video parameters and tune the TV tuner. This device can be used with xawtv -or other V4L(2) video software as a standard uncompressed device. - -The second device (most likely /dev/video1) provides access to the -compression functions of the GO7007. It can be tested using the gorecord -application in the apps/ directory of this SDK: - - $ apps/gorecord -vdevice /dev/video1 -noaudio test.avi - -Currently the frame resolution is fixed at 720x480 (NTSC) or 720x576 (PAL), -and the video standard must be specified to both the raw and the compressed -video devices (xawtv and gorecord, for example). - - --------------------------------------------------------------------------- -RELEASE NOTES FOR WIS GO7007SB LINUX DRIVER ---------------------------------------------------------------------------- - -Last updated: 5 November 2005 - - - Release 0.9.7 includes new support for using udev to run fxload. The - install script should automatically detect whether the old hotplug - scripts or the new udev rules should be used. To force the use of - hotplug, run "make install USE_UDEV=n". To force the use of udev, run - "make install USE_UDEV=y". - - - Motion detection is supported but undocumented. Try the `modet` app - for a demonstration of how to use the facility. - - - Using USB2.0 devices such as the TV402U with USB1.1 HCDs or hubs can - cause buffer overruns and frame drops, even at low framerates, due to - inconsistency in the bitrate control mechanism. - - - On devices with an SAA7115, including the Plextor ConvertX, video height - values of 96, 128, 160, 192, 256, 320, and 384 do not work in NTSC mode. - All valid heights up to 512 work correctly in PAL mode. - - - The WIS Star Trek and PCI Voyager boards have no support yet for audio - or the TV tuner. diff --git a/drivers/staging/go7007/s2250-board.c b/drivers/staging/go7007/s2250-board.c deleted file mode 100644 index e7736a915530..000000000000 --- a/drivers/staging/go7007/s2250-board.c +++ /dev/null @@ -1,698 +0,0 @@ -/* - * Copyright (C) 2008 Sensoray Company 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "go7007-priv.h" - -MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver"); -MODULE_LICENSE("GPL v2"); - -#define TLV320_ADDRESS 0x34 -#define VPX322_ADDR_ANALOGCONTROL1 0x02 -#define VPX322_ADDR_BRIGHTNESS0 0x0127 -#define VPX322_ADDR_BRIGHTNESS1 0x0131 -#define VPX322_ADDR_CONTRAST0 0x0128 -#define VPX322_ADDR_CONTRAST1 0x0132 -#define VPX322_ADDR_HUE 0x00dc -#define VPX322_ADDR_SAT 0x0030 - -struct go7007_usb_board { - unsigned int flags; - struct go7007_board_info main_info; -}; - -struct go7007_usb { - struct go7007_usb_board *board; - struct mutex i2c_lock; - struct usb_device *usbdev; - struct urb *video_urbs[8]; - struct urb *audio_urbs[8]; - struct urb *intr_urb; -}; - -static unsigned char aud_regs[] = { - 0x1e, 0x00, - 0x00, 0x17, - 0x02, 0x17, - 0x04, 0xf9, - 0x06, 0xf9, - 0x08, 0x02, - 0x0a, 0x00, - 0x0c, 0x00, - 0x0a, 0x00, - 0x0c, 0x00, - 0x0e, 0x02, - 0x10, 0x00, - 0x12, 0x01, - 0x00, 0x00, -}; - - -static unsigned char vid_regs[] = { - 0xF2, 0x0f, - 0xAA, 0x00, - 0xF8, 0xff, - 0x00, 0x00, -}; - -static u16 vid_regs_fp[] = { - 0x028, 0x067, - 0x120, 0x016, - 0x121, 0xcF2, - 0x122, 0x0F2, - 0x123, 0x00c, - 0x124, 0x2d0, - 0x125, 0x2e0, - 0x126, 0x004, - 0x128, 0x1E0, - 0x12A, 0x016, - 0x12B, 0x0F2, - 0x12C, 0x0F2, - 0x12D, 0x00c, - 0x12E, 0x2d0, - 0x12F, 0x2e0, - 0x130, 0x004, - 0x132, 0x1E0, - 0x140, 0x060, - 0x153, 0x00C, - 0x154, 0x200, - 0x150, 0x801, - 0x000, 0x000 -}; - -/* PAL specific values */ -static u16 vid_regs_fp_pal[] = -{ - 0x120, 0x017, - 0x121, 0xd22, - 0x122, 0x122, - 0x12A, 0x017, - 0x12B, 0x122, - 0x12C, 0x122, - 0x140, 0x060, - 0x000, 0x000, -}; - -struct s2250 { - struct v4l2_subdev sd; - v4l2_std_id std; - int input; - int brightness; - int contrast; - int saturation; - int hue; - int reg12b_val; - int audio_input; - struct i2c_client *audio; -}; - -static inline struct s2250 *to_state(struct v4l2_subdev *sd) -{ - return container_of(sd, struct s2250, sd); -} - -/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ -static int go7007_usb_vendor_request(struct go7007 *go, u16 request, - u16 value, u16 index, void *transfer_buffer, int length, int in) -{ - struct go7007_usb *usb = go->hpi_context; - int timeout = 5000; - - if (in) { - return usb_control_msg(usb->usbdev, - usb_rcvctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, - value, index, transfer_buffer, length, timeout); - } else { - return usb_control_msg(usb->usbdev, - usb_sndctrlpipe(usb->usbdev, 0), request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, index, transfer_buffer, length, timeout); - } -} -/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - struct go7007 *go = i2c_get_adapdata(client->adapter); - struct go7007_usb *usb; - int rc; - int dev_addr = client->addr << 1; /* firmware wants 8-bit address */ - u8 *buf; - - if (go == NULL) - return -ENODEV; - - if (go->status == STATUS_SHUTDOWN) - return -EBUSY; - - buf = kzalloc(16, GFP_KERNEL); - if (buf == NULL) - return -ENOMEM; - - usb = go->hpi_context; - if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); - kfree(buf); - return -EINTR; - } - rc = go7007_usb_vendor_request(go, 0x55, dev_addr, - (reg<<8 | value), - buf, - 16, 1); - - mutex_unlock(&usb->i2c_lock); - kfree(buf); - return rc; -} - -static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) -{ - struct go7007 *go = i2c_get_adapdata(client->adapter); - struct go7007_usb *usb; - u8 *buf; - struct s2250 *dec = i2c_get_clientdata(client); - - if (go == NULL) - return -ENODEV; - - if (go->status == STATUS_SHUTDOWN) - return -EBUSY; - - buf = kzalloc(16, GFP_KERNEL); - - if (buf == NULL) - return -ENOMEM; - - - - memset(buf, 0xcd, 6); - - usb = go->hpi_context; - if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); - kfree(buf); - return -EINTR; - } - if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) { - kfree(buf); - return -EFAULT; - } - - mutex_unlock(&usb->i2c_lock); - if (buf[0] == 0) { - unsigned int subaddr, val_read; - - subaddr = (buf[4] << 8) + buf[5]; - val_read = (buf[2] << 8) + buf[3]; - kfree(buf); - if (val_read != val) { - printk(KERN_INFO "invalid fp write %x %x\n", - val_read, val); - return -EFAULT; - } - if (subaddr != addr) { - printk(KERN_INFO "invalid fp write addr %x %x\n", - subaddr, addr); - return -EFAULT; - } - } else { - kfree(buf); - return -EFAULT; - } - - /* save last 12b value */ - if (addr == 0x12b) - dec->reg12b_val = val; - - return 0; -} - -static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) -{ - struct go7007 *go = i2c_get_adapdata(client->adapter); - struct go7007_usb *usb; - u8 *buf; - - if (go == NULL) - return -ENODEV; - - if (go->status == STATUS_SHUTDOWN) - return -EBUSY; - - buf = kzalloc(16, GFP_KERNEL); - - if (buf == NULL) - return -ENOMEM; - - - - memset(buf, 0xcd, 6); - usb = go->hpi_context; - if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { - printk(KERN_INFO "i2c lock failed\n"); - kfree(buf); - return -EINTR; - } - if (go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1) < 0) { - kfree(buf); - return -EFAULT; - } - mutex_unlock(&usb->i2c_lock); - - *val = (buf[0] << 8) | buf[1]; - kfree(buf); - - return 0; -} - - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { - if (write_reg(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed\n"); - return -1; - } - } - return 0; -} - -static int write_regs_fp(struct i2c_client *client, u16 *regs) -{ - int i; - - for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { - if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { - printk(KERN_INFO "s2250: failed fp\n"); - return -1; - } - } - return 0; -} - - -/* ------------------------------------------------------------------------- */ - -static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, - u32 config) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int vidsys; - - vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00; - if (input == 0) { - /* composite */ - write_reg_fp(client, 0x20, 0x020 | vidsys); - write_reg_fp(client, 0x21, 0x662); - write_reg_fp(client, 0x140, 0x060); - } else if (input == 1) { - /* S-Video */ - write_reg_fp(client, 0x20, 0x040 | vidsys); - write_reg_fp(client, 0x21, 0x666); - write_reg_fp(client, 0x140, 0x060); - } else { - return -EINVAL; - } - state->input = input; - return 0; -} - -static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - u16 vidsource; - - vidsource = (state->input == 1) ? 0x040 : 0x020; - switch (norm) { - case V4L2_STD_NTSC: - write_regs_fp(client, vid_regs_fp); - write_reg_fp(client, 0x20, vidsource | 1); - break; - case V4L2_STD_PAL: - write_regs_fp(client, vid_regs_fp); - write_regs_fp(client, vid_regs_fp_pal); - write_reg_fp(client, 0x20, vidsource); - break; - default: - return -EINVAL; - } - state->std = norm; - return 0; -} - -static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query) -{ - switch (query->id) { - case V4L2_CID_BRIGHTNESS: - return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); - case V4L2_CID_CONTRAST: - return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); - case V4L2_CID_SATURATION: - return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(query, -50, 50, 1, 0); - default: - return -EINVAL; - } - return 0; -} - -static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - int value1; - u16 oldvalue; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 100) - state->brightness = 100; - else if (ctrl->value < 0) - state->brightness = 0; - else - state->brightness = ctrl->value; - value1 = (state->brightness - 50) * 255 / 100; - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, - value1 | (oldvalue & ~0xff)); - read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, - value1 | (oldvalue & ~0xff)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 100) - state->contrast = 100; - else if (ctrl->value < 0) - state->contrast = 0; - else - state->contrast = ctrl->value; - value1 = state->contrast * 0x40 / 100; - if (value1 > 0x3f) - value1 = 0x3f; /* max */ - read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST0, - value1 | (oldvalue & ~0x3f)); - read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); - write_reg_fp(client, VPX322_ADDR_CONTRAST1, - value1 | (oldvalue & ~0x3f)); - write_reg_fp(client, 0x140, 0x60); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 100) - state->saturation = 100; - else if (ctrl->value < 0) - state->saturation = 0; - else - state->saturation = ctrl->value; - value1 = state->saturation * 4140 / 100; - if (value1 > 4094) - value1 = 4094; - write_reg_fp(client, VPX322_ADDR_SAT, value1); - break; - case V4L2_CID_HUE: - if (ctrl->value > 50) - state->hue = 50; - else if (ctrl->value < -50) - state->hue = -50; - else - state->hue = ctrl->value; - /* clamp the hue range */ - value1 = state->hue * 280 / 50; - write_reg_fp(client, VPX322_ADDR_HUE, value1); - break; - default: - return -EINVAL; - } - return 0; -} - -static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) -{ - struct s2250 *state = to_state(sd); - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = state->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = state->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = state->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = state->hue; - break; - default: - return -EINVAL; - } - return 0; -} - -static int s2250_s_mbus_fmt(struct v4l2_subdev *sd, - struct v4l2_mbus_framefmt *fmt) -{ - struct s2250 *state = to_state(sd); - struct i2c_client *client = v4l2_get_subdevdata(sd); - - if (fmt->height < 640) { - write_reg_fp(client, 0x12b, state->reg12b_val | 0x400); - write_reg_fp(client, 0x140, 0x060); - } else { - write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400); - write_reg_fp(client, 0x140, 0x060); - } - return 0; -} - -static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, - u32 config) -{ - struct s2250 *state = to_state(sd); - - switch (input) { - case 0: - write_reg(state->audio, 0x08, 0x02); /* Line In */ - break; - case 1: - write_reg(state->audio, 0x08, 0x04); /* Mic */ - break; - case 2: - write_reg(state->audio, 0x08, 0x05); /* Mic Boost */ - break; - default: - return -EINVAL; - } - state->audio_input = input; - return 0; -} - - -static int s2250_log_status(struct v4l2_subdev *sd) -{ - struct s2250 *state = to_state(sd); - - v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" : - state->std == V4L2_STD_PAL ? "PAL" : - state->std == V4L2_STD_SECAM ? "SECAM" : - "unknown"); - v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" : - state->input == 1 ? "S-video" : - "error"); - v4l2_info(sd, "Brightness: %d\n", state->brightness); - v4l2_info(sd, "Contrast: %d\n", state->contrast); - v4l2_info(sd, "Saturation: %d\n", state->saturation); - v4l2_info(sd, "Hue: %d\n", state->hue); return 0; - v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : - state->audio_input == 1 ? "Mic" : - state->audio_input == 2 ? "Mic Boost" : - "error"); - return 0; -} - -/* --------------------------------------------------------------------------*/ - -static const struct v4l2_subdev_core_ops s2250_core_ops = { - .log_status = s2250_log_status, - .g_ctrl = s2250_g_ctrl, - .s_ctrl = s2250_s_ctrl, - .queryctrl = s2250_queryctrl, - .s_std = s2250_s_std, -}; - -static const struct v4l2_subdev_audio_ops s2250_audio_ops = { - .s_routing = s2250_s_audio_routing, -}; - -static const struct v4l2_subdev_video_ops s2250_video_ops = { - .s_routing = s2250_s_video_routing, - .s_mbus_fmt = s2250_s_mbus_fmt, -}; - -static const struct v4l2_subdev_ops s2250_ops = { - .core = &s2250_core_ops, - .audio = &s2250_audio_ops, - .video = &s2250_video_ops, -}; - -/* --------------------------------------------------------------------------*/ - -static int s2250_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_client *audio; - struct i2c_adapter *adapter = client->adapter; - struct s2250 *state; - struct v4l2_subdev *sd; - u8 *data; - struct go7007 *go = i2c_get_adapdata(adapter); - struct go7007_usb *usb = go->hpi_context; - - audio = i2c_new_dummy(adapter, TLV320_ADDRESS >> 1); - if (audio == NULL) - return -ENOMEM; - - state = kmalloc(sizeof(struct s2250), GFP_KERNEL); - if (state == NULL) { - i2c_unregister_device(audio); - return -ENOMEM; - } - - sd = &state->sd; - v4l2_i2c_subdev_init(sd, client, &s2250_ops); - - v4l2_info(sd, "initializing %s at address 0x%x on %s\n", - "Sensoray 2250/2251", client->addr, client->adapter->name); - - state->std = V4L2_STD_NTSC; - state->brightness = 50; - state->contrast = 50; - state->saturation = 50; - state->hue = 0; - state->audio = audio; - - /* initialize the audio */ - if (write_regs(audio, aud_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing audio\n"); - i2c_unregister_device(audio); - kfree(state); - return 0; - } - - if (write_regs(client, vid_regs) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); - i2c_unregister_device(audio); - kfree(state); - return 0; - } - if (write_regs_fp(client, vid_regs_fp) < 0) { - printk(KERN_ERR - "s2250: error initializing decoder\n"); - i2c_unregister_device(audio); - kfree(state); - return 0; - } - /* set default channel */ - /* composite */ - write_reg_fp(client, 0x20, 0x020 | 1); - write_reg_fp(client, 0x21, 0x662); - write_reg_fp(client, 0x140, 0x060); - - /* set default audio input */ - state->audio_input = 0; - write_reg(client, 0x08, 0x02); /* Line In */ - - if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { - data = kzalloc(16, GFP_KERNEL); - if (data != NULL) { - int rc; - rc = go7007_usb_vendor_request(go, 0x41, 0, 0, - data, 16, 1); - if (rc > 0) { - u8 mask; - data[0] = 0; - mask = 1<<5; - data[0] &= ~mask; - data[1] |= mask; - go7007_usb_vendor_request(go, 0x40, 0, - (data[1]<<8) - + data[1], - data, 16, 0); - } - kfree(data); - } - mutex_unlock(&usb->i2c_lock); - } - - v4l2_info(sd, "initialized successfully\n"); - return 0; -} - -static int s2250_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 s2250_id[] = { - { "s2250", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, s2250_id); - -static struct i2c_driver s2250_driver = { - .driver = { - .owner = THIS_MODULE, - .name = "s2250", - }, - .probe = s2250_probe, - .remove = s2250_remove, - .id_table = s2250_id, -}; - -static __init int init_s2250(void) -{ - return i2c_add_driver(&s2250_driver); -} - -static __exit void exit_s2250(void) -{ - i2c_del_driver(&s2250_driver); -} - -module_init(init_s2250); -module_exit(exit_s2250); diff --git a/drivers/staging/go7007/s2250-loader.c b/drivers/staging/go7007/s2250-loader.c deleted file mode 100644 index 4e132519e253..000000000000 --- a/drivers/staging/go7007/s2250-loader.c +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2008 Sensoray Company 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include - -#define S2250_LOADER_FIRMWARE "s2250_loader.fw" -#define S2250_FIRMWARE "s2250.fw" - -typedef struct device_extension_s { - struct kref kref; - int minor; - struct usb_device *usbdev; -} device_extension_t, *pdevice_extension_t; - -#define USB_s2250loader_MAJOR 240 -#define USB_s2250loader_MINOR_BASE 0 -#define MAX_DEVICES 256 - -static pdevice_extension_t s2250_dev_table[MAX_DEVICES]; -static DEFINE_MUTEX(s2250_dev_table_mutex); - -#define to_s2250loader_dev_common(d) container_of(d, device_extension_t, kref) -static void s2250loader_delete(struct kref *kref) -{ - pdevice_extension_t s = to_s2250loader_dev_common(kref); - s2250_dev_table[s->minor] = NULL; - kfree(s); -} - -static int s2250loader_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *usbdev; - int minor, ret; - pdevice_extension_t s = NULL; - const struct firmware *fw; - - usbdev = usb_get_dev(interface_to_usbdev(interface)); - if (!usbdev) { - printk(KERN_ERR "Enter s2250loader_probe failed\n"); - return -1; - } - printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n"); - printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n", - usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, - usbdev->devnum); - - if (usbdev->descriptor.bNumConfigurations != 1) { - printk(KERN_ERR "can't handle multiple config\n"); - return -1; - } - mutex_lock(&s2250_dev_table_mutex); - - for (minor = 0; minor < MAX_DEVICES; minor++) { - if (s2250_dev_table[minor] == NULL) - break; - } - - if (minor < 0 || minor >= MAX_DEVICES) { - printk(KERN_ERR "Invalid minor: %d\n", minor); - goto failed; - } - - /* Allocate dev data structure */ - s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); - if (s == NULL) { - printk(KERN_ERR "Out of memory\n"); - goto failed; - } - s2250_dev_table[minor] = s; - - printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n", - usbdev->devnum, usbdev->bus->busnum, minor); - - memset(s, 0, sizeof(device_extension_t)); - s->usbdev = usbdev; - printk(KERN_INFO "loading 2250 loader\n"); - - kref_init(&(s->kref)); - - mutex_unlock(&s2250_dev_table_mutex); - - if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR - "s2250: unable to load firmware from file \"%s\"\n", - S2250_LOADER_FIRMWARE); - goto failed2; - } - ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); - release_firmware(fw); - if (0 != ret) { - printk(KERN_ERR "loader download failed\n"); - goto failed2; - } - - if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { - printk(KERN_ERR - "s2250: unable to load firmware from file \"%s\"\n", - S2250_FIRMWARE); - goto failed2; - } - ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); - release_firmware(fw); - if (0 != ret) { - printk(KERN_ERR "firmware_s2250 download failed\n"); - goto failed2; - } - - usb_set_intfdata(interface, s); - return 0; - -failed: - mutex_unlock(&s2250_dev_table_mutex); -failed2: - if (s) - kref_put(&(s->kref), s2250loader_delete); - - printk(KERN_ERR "probe failed\n"); - return -1; -} - -static void s2250loader_disconnect(struct usb_interface *interface) -{ - pdevice_extension_t s; - printk(KERN_INFO "s2250: disconnect\n"); - s = usb_get_intfdata(interface); - usb_set_intfdata(interface, NULL); - kref_put(&(s->kref), s2250loader_delete); -} - -static const struct usb_device_id s2250loader_ids[] = { - {USB_DEVICE(0x1943, 0xa250)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, s2250loader_ids); - -static struct usb_driver s2250loader_driver = { - .name = "s2250-loader", - .probe = s2250loader_probe, - .disconnect = s2250loader_disconnect, - .id_table = s2250loader_ids, -}; - -static int __init s2250loader_init(void) -{ - int r; - unsigned i = 0; - - for (i = 0; i < MAX_DEVICES; i++) - s2250_dev_table[i] = NULL; - - r = usb_register(&s2250loader_driver); - if (r) { - printk(KERN_ERR "usb_register failed. Error number %d\n", r); - return -1; - } - - printk(KERN_INFO "s2250loader_init: driver registered\n"); - return 0; -} -module_init(s2250loader_init); - -static void __exit s2250loader_cleanup(void) -{ - printk(KERN_INFO "s2250loader_cleanup\n"); - usb_deregister(&s2250loader_driver); -} -module_exit(s2250loader_cleanup); - -MODULE_AUTHOR(""); -MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251"); -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/s2250-loader.h b/drivers/staging/go7007/s2250-loader.h deleted file mode 100644 index b7c301af16cc..000000000000 --- a/drivers/staging/go7007/s2250-loader.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#ifndef _S2250_LOADER_H_ -#define _S2250_LOADER_H_ - -extern int s2250loader_init(void); -extern void s2250loader_cleanup(void); - -#endif diff --git a/drivers/staging/go7007/saa7134-go7007.c b/drivers/staging/go7007/saa7134-go7007.c deleted file mode 100644 index cf7c34a99459..000000000000 --- a/drivers/staging/go7007/saa7134-go7007.c +++ /dev/null @@ -1,532 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "saa7134-reg.h" -#include "saa7134.h" -#include "go7007-priv.h" - -#define GO7007_HPI_DEBUG - -enum hpi_address { - HPI_ADDR_VIDEO_BUFFER = 0xe4, - HPI_ADDR_INIT_BUFFER = 0xea, - HPI_ADDR_INTR_RET_VALUE = 0xee, - HPI_ADDR_INTR_RET_DATA = 0xec, - HPI_ADDR_INTR_STATUS = 0xf4, - HPI_ADDR_INTR_WR_PARAM = 0xf6, - HPI_ADDR_INTR_WR_INDEX = 0xf8, -}; - -enum gpio_command { - GPIO_COMMAND_RESET = 0x00, /* 000b */ - GPIO_COMMAND_REQ1 = 0x04, /* 001b */ - GPIO_COMMAND_WRITE = 0x20, /* 010b */ - GPIO_COMMAND_REQ2 = 0x24, /* 011b */ - GPIO_COMMAND_READ = 0x80, /* 100b */ - GPIO_COMMAND_VIDEO = 0x84, /* 101b */ - GPIO_COMMAND_IDLE = 0xA0, /* 110b */ - GPIO_COMMAND_ADDR = 0xA4, /* 111b */ -}; - -struct saa7134_go7007 { - struct saa7134_dev *dev; - u8 *top; - u8 *bottom; - dma_addr_t top_dma; - dma_addr_t bottom_dma; -}; - -static struct go7007_board_info board_voyager = { - .firmware = "go7007tv.bin", - .flags = 0, - .sensor_flags = GO7007_SENSOR_656 | - GO7007_SENSOR_VALID_ENABLE | - GO7007_SENSOR_TV | - GO7007_SENSOR_VBI, - .audio_flags = GO7007_AUDIO_I2S_MODE_1 | - GO7007_AUDIO_WORD_16, - .audio_rate = 48000, - .audio_bclk_div = 8, - .audio_main_div = 2, - .hpi_buffer_cap = 7, - .num_inputs = 1, - .inputs = { - { - .name = "SAA7134", - }, - }, -}; -MODULE_FIRMWARE("go7007tv.bin"); - -/********************* Driver for GPIO HPI interface *********************/ - -static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data) -{ - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - - /* Write HPI address */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - /* Write low byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - /* Write high byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - return 0; -} - -static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data) -{ - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - - /* Write HPI address */ - saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); - - /* Read low byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - *data = saa_readb(SAA7134_GPIO_GPSTATUS0); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - /* Read high byte */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8; - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - - return 0; -} - -static int saa7134_go7007_interface_reset(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - u32 status; - u16 intr_val, intr_data; - int count = 20; - - saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */ - saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4); - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET); - msleep(1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); - msleep(10); - - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - - status = saa_readb(SAA7134_GPIO_GPSTATUS2); - /*printk(KERN_DEBUG "status is %s\n", status & 0x40 ? "OK" : "not OK"); */ - - /* enter command mode...(?) */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); - - do { - saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); - status = saa_readb(SAA7134_GPIO_GPSTATUS2); - /*printk(KERN_INFO "gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */ - } while (--count > 0); - - /* Wait for an interrupt to indicate successful hardware reset */ - if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || - (intr_val & ~0x1) != 0x55aa) { - printk(KERN_ERR - "saa7134-go7007: unable to reset the GO7007\n"); - return -1; - } - return 0; -} - -static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - int i; - u16 status_reg; - -#ifdef GO7007_HPI_DEBUG - printk(KERN_DEBUG - "saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data); -#endif - - for (i = 0; i < 100; ++i) { - gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); - if (!(status_reg & 0x0010)) - break; - msleep(10); - } - if (i == 100) { - printk(KERN_ERR - "saa7134-go7007: device is hung, status reg = 0x%04x\n", - status_reg); - return -1; - } - gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data); - gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr); - - return 0; -} - -static int saa7134_go7007_read_interrupt(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - - /* XXX we need to wait if there is no interrupt available */ - go->interrupt_available = 1; - gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value); - gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data); -#ifdef GO7007_HPI_DEBUG - printk(KERN_DEBUG "saa7134-go7007: ReadInterrupt: %04x %04x\n", - go->interrupt_value, go->interrupt_data); -#endif - return 0; -} - -static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev, - unsigned long status) -{ - struct go7007 *go = video_get_drvdata(dev->empress_dev); - struct saa7134_go7007 *saa = go->hpi_context; - - if (!go->streaming) - return; - if (0 != (status & 0x000f0000)) - printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n", - (status >> 16) & 0x0f); - if (status & 0x100000) { - dma_sync_single_for_cpu(&dev->pci->dev, - saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE); - go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE); - saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma)); - } else { - dma_sync_single_for_cpu(&dev->pci->dev, - saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE); - go7007_parse_video_stream(go, saa->top, PAGE_SIZE); - saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma)); - } -} - -static int saa7134_go7007_stream_start(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - - saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top), - 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (!saa->top_dma) - return -ENOMEM; - saa->bottom_dma = dma_map_page(&dev->pci->dev, - virt_to_page(saa->bottom), - 0, PAGE_SIZE, DMA_FROM_DEVICE); - if (!saa->bottom_dma) { - dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, - DMA_FROM_DEVICE); - return -ENOMEM; - } - - saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000); - saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200); - - /* Set HPI interface for video */ - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); - - /* Enable TS interface */ - saa_writeb(SAA7134_TS_PARALLEL, 0xe6); - - /* Reset TS interface */ - saa_setb(SAA7134_TS_SERIAL1, 0x01); - saa_clearb(SAA7134_TS_SERIAL1, 0x01); - - /* Set up transfer block size */ - saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1); - saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1); - saa_writeb(SAA7134_TS_DMA1, 0); - saa_writeb(SAA7134_TS_DMA2, 0); - - /* Enable video streaming mode */ - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO); - - saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma)); - saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma)); - saa_writel(SAA7134_RS_PITCH(5), 128); - saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX); - - /* Enable TS FIFO */ - saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); - - /* Enable DMA IRQ */ - saa_setl(SAA7134_IRQ1, - SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); - - return 0; -} - -static int saa7134_go7007_stream_stop(struct go7007 *go) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev; - - if (!saa) - return -EINVAL; - dev = saa->dev; - if (!dev) - return -EINVAL; - - /* Shut down TS FIFO */ - saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); - - /* Disable DMA IRQ */ - saa_clearl(SAA7134_IRQ1, - SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); - - /* Disable TS interface */ - saa_clearb(SAA7134_TS_PARALLEL, 0x80); - - dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, - DMA_FROM_DEVICE); - dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE, - DMA_FROM_DEVICE); - - return 0; -} - -static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - u16 status_reg; - int i; - -#ifdef GO7007_HPI_DEBUG - printk(KERN_DEBUG "saa7134-go7007: DownloadBuffer " - "sending %d bytes\n", len); -#endif - - while (len > 0) { - i = len > 64 ? 64 : len; - saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); - saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - while (i-- > 0) { - saa_writeb(SAA7134_GPIO_GPSTATUS0, *data); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); - saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); - ++data; - --len; - } - for (i = 0; i < 100; ++i) { - gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); - if (!(status_reg & 0x0002)) - break; - } - if (i == 100) { - printk(KERN_ERR "saa7134-go7007: device is hung, " - "status reg = 0x%04x\n", status_reg); - return -1; - } - } - return 0; -} - -static int saa7134_go7007_send_command(struct go7007 *go, unsigned int cmd, - void *arg) -{ - struct saa7134_go7007 *saa = go->hpi_context; - struct saa7134_dev *dev = saa->dev; - - switch (cmd) { - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; - return saa7134_s_std_internal(dev, NULL, std); - } - case VIDIOC_G_STD: - { - v4l2_std_id *std = arg; - *std = dev->tvnorm->id; - return 0; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) - return saa7134_queryctrl(NULL, NULL, ctrl); - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) - return saa7134_g_ctrl_internal(dev, NULL, ctrl); - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) - return saa7134_s_ctrl_internal(dev, NULL, ctrl); - } - } - return -EINVAL; - -} - -static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { - .interface_reset = saa7134_go7007_interface_reset, - .write_interrupt = saa7134_go7007_write_interrupt, - .read_interrupt = saa7134_go7007_read_interrupt, - .stream_start = saa7134_go7007_stream_start, - .stream_stop = saa7134_go7007_stream_stop, - .send_firmware = saa7134_go7007_send_firmware, - .send_command = saa7134_go7007_send_command, -}; - -/********************* Add/remove functions *********************/ - -static int saa7134_go7007_init(struct saa7134_dev *dev) -{ - struct go7007 *go; - struct saa7134_go7007 *saa; - - printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n"); - - saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL); - if (saa == NULL) - return -ENOMEM; - - /* Allocate a couple pages for receiving the compressed stream */ - saa->top = (u8 *)get_zeroed_page(GFP_KERNEL); - if (!saa->top) - goto allocfail; - saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL); - if (!saa->bottom) - goto allocfail; - - go = go7007_alloc(&board_voyager, &dev->pci->dev); - if (go == NULL) - goto allocfail; - go->board_id = GO7007_BOARDID_PCI_VOYAGER; - strncpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name)); - go->hpi_ops = &saa7134_go7007_hpi_ops; - go->hpi_context = saa; - saa->dev = dev; - - /* Boot the GO7007 */ - if (go7007_boot_encoder(go, go->board_info->flags & - GO7007_BOARD_USE_ONBOARD_I2C) < 0) - goto initfail; - - /* Do any final GO7007 initialization, then register the - * V4L2 and ALSA interfaces */ - if (go7007_register_encoder(go) < 0) - goto initfail; - dev->empress_dev = go->video_dev; - video_set_drvdata(dev->empress_dev, go); - - go->status = STATUS_ONLINE; - return 0; - -initfail: - go->status = STATUS_SHUTDOWN; - return 0; - -allocfail: - if (saa->top) - free_page((unsigned long)saa->top); - if (saa->bottom) - free_page((unsigned long)saa->bottom); - kfree(saa); - return -ENOMEM; -} - -static int saa7134_go7007_fini(struct saa7134_dev *dev) -{ - struct go7007 *go; - struct saa7134_go7007 *saa; - - if (NULL == dev->empress_dev) - return 0; - - go = video_get_drvdata(dev->empress_dev); - saa = go->hpi_context; - go->status = STATUS_SHUTDOWN; - free_page((unsigned long)saa->top); - free_page((unsigned long)saa->bottom); - kfree(saa); - go7007_remove(go); - dev->empress_dev = NULL; - - return 0; -} - -static struct saa7134_mpeg_ops saa7134_go7007_ops = { - .type = SAA7134_MPEG_GO7007, - .init = saa7134_go7007_init, - .fini = saa7134_go7007_fini, - .irq_ts_done = saa7134_go7007_irq_ts_done, -}; - -static int __init saa7134_go7007_mod_init(void) -{ - return saa7134_ts_register(&saa7134_go7007_ops); -} - -static void __exit saa7134_go7007_mod_cleanup(void) -{ - saa7134_ts_unregister(&saa7134_go7007_ops); -} - -module_init(saa7134_go7007_mod_init); -module_exit(saa7134_go7007_mod_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/snd-go7007.c b/drivers/staging/go7007/snd-go7007.c deleted file mode 100644 index deac938d8505..000000000000 --- a/drivers/staging/go7007/snd-go7007.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "go7007-priv.h" - -static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; -static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; -static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; - -module_param_array(index, int, NULL, 0444); -module_param_array(id, charp, NULL, 0444); -module_param_array(enable, bool, NULL, 0444); -MODULE_PARM_DESC(index, "Index value for the go7007 audio driver"); -MODULE_PARM_DESC(id, "ID string for the go7007 audio driver"); -MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver"); - -struct go7007_snd { - struct snd_card *card; - struct snd_pcm *pcm; - struct snd_pcm_substream *substream; - spinlock_t lock; - int w_idx; - int hw_ptr; - int avail; - int capturing; -}; - -static struct snd_pcm_hardware go7007_snd_capture_hw = { - .info = (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_48000, - .rate_min = 48000, - .rate_max = 48000, - .channels_min = 2, - .channels_max = 2, - .buffer_bytes_max = (128*1024), - .period_bytes_min = 4096, - .period_bytes_max = (128*1024), - .periods_min = 1, - .periods_max = 32, -}; - -static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) -{ - struct go7007_snd *gosnd = go->snd_context; - struct snd_pcm_runtime *runtime = gosnd->substream->runtime; - int frames = bytes_to_frames(runtime, length); - - spin_lock(&gosnd->lock); - gosnd->hw_ptr += frames; - if (gosnd->hw_ptr >= runtime->buffer_size) - gosnd->hw_ptr -= runtime->buffer_size; - gosnd->avail += frames; - spin_unlock(&gosnd->lock); - if (gosnd->w_idx + length > runtime->dma_bytes) { - int cpy = runtime->dma_bytes - gosnd->w_idx; - - memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy); - length -= cpy; - buf += cpy; - gosnd->w_idx = 0; - } - memcpy(runtime->dma_area + gosnd->w_idx, buf, length); - gosnd->w_idx += length; - spin_lock(&gosnd->lock); - if (gosnd->avail < runtime->period_size) { - spin_unlock(&gosnd->lock); - return; - } - gosnd->avail -= runtime->period_size; - spin_unlock(&gosnd->lock); - if (gosnd->capturing) - snd_pcm_period_elapsed(gosnd->substream); -} - -static int go7007_snd_hw_params(struct snd_pcm_substream *substream, - struct snd_pcm_hw_params *hw_params) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - unsigned int bytes; - - bytes = params_buffer_bytes(hw_params); - if (substream->runtime->dma_bytes > 0) - vfree(substream->runtime->dma_area); - substream->runtime->dma_bytes = 0; - substream->runtime->dma_area = vmalloc(bytes); - if (substream->runtime->dma_area == NULL) - return -ENOMEM; - substream->runtime->dma_bytes = bytes; - go->audio_deliver = parse_audio_stream_data; - return 0; -} - -static int go7007_snd_hw_free(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - - go->audio_deliver = NULL; - if (substream->runtime->dma_bytes > 0) - vfree(substream->runtime->dma_area); - substream->runtime->dma_bytes = 0; - return 0; -} - -static int go7007_snd_capture_open(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - unsigned long flags; - int r; - - spin_lock_irqsave(&gosnd->lock, flags); - if (gosnd->substream == NULL) { - gosnd->substream = substream; - substream->runtime->hw = go7007_snd_capture_hw; - r = 0; - } else - r = -EBUSY; - spin_unlock_irqrestore(&gosnd->lock, flags); - return r; -} - -static int go7007_snd_capture_close(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - - gosnd->substream = NULL; - return 0; -} - -static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream) -{ - return 0; -} - -static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - /* Just set a flag to indicate we should signal ALSA when - * sound comes in */ - gosnd->capturing = 1; - return 0; - case SNDRV_PCM_TRIGGER_STOP: - gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; - gosnd->capturing = 0; - return 0; - default: - return -EINVAL; - } -} - -static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream) -{ - struct go7007 *go = snd_pcm_substream_chip(substream); - struct go7007_snd *gosnd = go->snd_context; - - return gosnd->hw_ptr; -} - -static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream, - unsigned long offset) -{ - return vmalloc_to_page(substream->runtime->dma_area + offset); -} - -static struct snd_pcm_ops go7007_snd_capture_ops = { - .open = go7007_snd_capture_open, - .close = go7007_snd_capture_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = go7007_snd_hw_params, - .hw_free = go7007_snd_hw_free, - .prepare = go7007_snd_pcm_prepare, - .trigger = go7007_snd_pcm_trigger, - .pointer = go7007_snd_pcm_pointer, - .page = go7007_snd_pcm_page, -}; - -static int go7007_snd_free(struct snd_device *device) -{ - struct go7007 *go = device->device_data; - - kfree(go->snd_context); - go->snd_context = NULL; - if (--go->ref_count == 0) - kfree(go); - return 0; -} - -static struct snd_device_ops go7007_snd_device_ops = { - .dev_free = go7007_snd_free, -}; - -int go7007_snd_init(struct go7007 *go) -{ - static int dev; - struct go7007_snd *gosnd; - int ret = 0; - - if (dev >= SNDRV_CARDS) - return -ENODEV; - if (!enable[dev]) { - dev++; - return -ENOENT; - } - gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL); - if (gosnd == NULL) - return -ENOMEM; - spin_lock_init(&gosnd->lock); - gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; - gosnd->capturing = 0; - ret = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, - &gosnd->card); - if (ret < 0) { - kfree(gosnd); - return ret; - } - ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go, - &go7007_snd_device_ops); - if (ret < 0) { - kfree(gosnd); - return ret; - } - snd_card_set_dev(gosnd->card, go->dev); - ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm); - if (ret < 0) { - snd_card_free(gosnd->card); - kfree(gosnd); - return ret; - } - strncpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver)); - strncpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver)); - strncpy(gosnd->card->longname, gosnd->card->shortname, - sizeof(gosnd->card->longname)); - - gosnd->pcm->private_data = go; - snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE, - &go7007_snd_capture_ops); - - ret = snd_card_register(gosnd->card); - if (ret < 0) { - snd_card_free(gosnd->card); - kfree(gosnd); - return ret; - } - - gosnd->substream = NULL; - go->snd_context = gosnd; - ++dev; - ++go->ref_count; - - return 0; -} -EXPORT_SYMBOL(go7007_snd_init); - -int go7007_snd_remove(struct go7007 *go) -{ - struct go7007_snd *gosnd = go->snd_context; - - snd_card_disconnect(gosnd->card); - snd_card_free_when_closed(gosnd->card); - return 0; -} -EXPORT_SYMBOL(go7007_snd_remove); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/wis-i2c.h b/drivers/staging/go7007/wis-i2c.h deleted file mode 100644 index 3c2b9be455df..000000000000 --- a/drivers/staging/go7007/wis-i2c.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -/* Temporary I2C IDs -- these need to be replaced with real registered IDs */ -#define I2C_DRIVERID_WIS_SAA7115 0xf0f0 -#define I2C_DRIVERID_WIS_UDA1342 0xf0f1 -#define I2C_DRIVERID_WIS_SONY_TUNER 0xf0f2 -#define I2C_DRIVERID_WIS_TW9903 0xf0f3 -#define I2C_DRIVERID_WIS_SAA7113 0xf0f4 -#define I2C_DRIVERID_WIS_OV7640 0xf0f5 -#define I2C_DRIVERID_WIS_TW2804 0xf0f6 -#define I2C_DRIVERID_S2250 0xf0f7 - -/* Flag to indicate that the client needs to be accessed with SCCB semantics */ -/* We re-use the I2C_M_TEN value so the flag passes through the masks in the - * core I2C code. Major kludge, but the I2C layer ain't exactly flexible. */ -#define I2C_CLIENT_SCCB 0x10 - -/* Definitions for new video decoder commands */ - -struct video_decoder_resolution { - unsigned int width; - unsigned int height; -}; - -#define DECODER_SET_RESOLUTION _IOW('d', 200, struct video_decoder_resolution) -#define DECODER_SET_CHANNEL _IOW('d', 201, int) - -/* Sony tuner types */ - -#define TUNER_SONY_BTF_PG472Z 200 -#define TUNER_SONY_BTF_PK467Z 201 -#define TUNER_SONY_BTF_PB463Z 202 diff --git a/drivers/staging/go7007/wis-ov7640.c b/drivers/staging/go7007/wis-ov7640.c deleted file mode 100644 index 6bc9470fecb6..000000000000 --- a/drivers/staging/go7007/wis-ov7640.c +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include - -#include "wis-i2c.h" - -struct wis_ov7640 { - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 initial_registers[] = -{ - 0x12, 0x80, - 0x12, 0x54, - 0x14, 0x24, - 0x15, 0x01, - 0x28, 0x20, - 0x75, 0x82, - 0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */ -}; - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0xFF; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_ov7640_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - client->flags = I2C_CLIENT_SCCB; - - printk(KERN_DEBUG - "wis-ov7640: initializing OV7640 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR "wis-ov7640: error initializing OV7640\n"); - return -ENODEV; - } - - return 0; -} - -static int wis_ov7640_remove(struct i2c_client *client) -{ - return 0; -} - -static const struct i2c_device_id wis_ov7640_id[] = { - { "wis_ov7640", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_ov7640_id); - -static struct i2c_driver wis_ov7640_driver = { - .driver = { - .name = "WIS OV7640 I2C driver", - }, - .probe = wis_ov7640_probe, - .remove = wis_ov7640_remove, - .id_table = wis_ov7640_id, -}; - -static int __init wis_ov7640_init(void) -{ - return i2c_add_driver(&wis_ov7640_driver); -} - -static void __exit wis_ov7640_cleanup(void) -{ - i2c_del_driver(&wis_ov7640_driver); -} - -module_init(wis_ov7640_init); -module_exit(wis_ov7640_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/wis-saa7113.c b/drivers/staging/go7007/wis-saa7113.c deleted file mode 100644 index 05e0e1083864..000000000000 --- a/drivers/staging/go7007/wis-saa7113.c +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "wis-i2c.h" - -struct wis_saa7113 { - int norm; - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 initial_registers[] = -{ - 0x01, 0x08, - 0x02, 0xc0, - 0x03, 0x33, - 0x04, 0x00, - 0x05, 0x00, - 0x06, 0xe9, - 0x07, 0x0d, - 0x08, 0xd8, - 0x09, 0x40, - 0x0a, 0x80, - 0x0b, 0x47, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x01, - 0x0f, 0x2a, - 0x10, 0x40, - 0x11, 0x0c, - 0x12, 0xfe, - 0x13, 0x00, - 0x14, 0x00, - 0x15, 0x04, - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0x00, - 0x19, 0x00, - 0x1a, 0x00, - 0x1b, 0x00, - 0x1c, 0x00, - 0x1d, 0x00, - 0x1e, 0x00, - 0x1f, 0xc8, - 0x40, 0x00, - 0x41, 0xff, - 0x42, 0xff, - 0x43, 0xff, - 0x44, 0xff, - 0x45, 0xff, - 0x46, 0xff, - 0x47, 0xff, - 0x48, 0xff, - 0x49, 0xff, - 0x4a, 0xff, - 0x4b, 0xff, - 0x4c, 0xff, - 0x4d, 0xff, - 0x4e, 0xff, - 0x4f, 0xff, - 0x50, 0xff, - 0x51, 0xff, - 0x52, 0xff, - 0x53, 0xff, - 0x54, 0xff, - 0x55, 0xff, - 0x56, 0xff, - 0x57, 0xff, - 0x58, 0x00, - 0x59, 0x54, - 0x5a, 0x07, - 0x5b, 0x83, - 0x5c, 0x00, - 0x5d, 0x00, - 0x5e, 0x00, - 0x5f, 0x00, - 0x60, 0x00, - 0x61, 0x00, - 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0x00; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_saa7113_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_saa7113 *dec = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_S_INPUT: - { - int *input = arg; - - i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input); - i2c_smbus_write_byte_data(client, 0x09, - *input < 6 ? 0x40 : 0x80); - break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - dec->norm = *input; - if (dec->norm & V4L2_STD_NTSC) { - write_reg(client, 0x0e, 0x01); - write_reg(client, 0x10, 0x40); - } else if (dec->norm & V4L2_STD_PAL) { - write_reg(client, 0x0e, 0x01); - write_reg(client, 0x10, 0x48); - } else if (dec->norm * V4L2_STD_SECAM) { - write_reg(client, 0x0e, 0x50); - write_reg(client, 0x10, 0x48); - } - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 71; - ctrl->flags = 0; - break; - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 255) - dec->brightness = 255; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - write_reg(client, 0x0a, dec->brightness); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 127) - dec->contrast = 127; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x0b, dec->contrast); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - write_reg(client, 0x0c, dec->saturation); - break; - case V4L2_CID_HUE: - if (ctrl->value > 127) - dec->hue = 127; - else if (ctrl->value < -128) - dec->hue = -128; - else - dec->hue = ctrl->value; - write_reg(client, 0x0d, dec->hue); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_saa7113_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_saa7113 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->norm = V4L2_STD_NTSC; - dec->brightness = 128; - dec->contrast = 71; - dec->saturation = 64; - dec->hue = 0; - i2c_set_clientdata(client, dec); - - printk(KERN_DEBUG - "wis-saa7113: initializing SAA7113 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR - "wis-saa7113: error initializing SAA7113\n"); - kfree(dec); - return -ENODEV; - } - - return 0; -} - -static int wis_saa7113_remove(struct i2c_client *client) -{ - struct wis_saa7113 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_saa7113_id[] = { - { "wis_saa7113", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_saa7113_id); - -static struct i2c_driver wis_saa7113_driver = { - .driver = { - .name = "WIS SAA7113 I2C driver", - }, - .probe = wis_saa7113_probe, - .remove = wis_saa7113_remove, - .command = wis_saa7113_command, - .id_table = wis_saa7113_id, -}; - -static int __init wis_saa7113_init(void) -{ - return i2c_add_driver(&wis_saa7113_driver); -} - -static void __exit wis_saa7113_cleanup(void) -{ - i2c_del_driver(&wis_saa7113_driver); -} - -module_init(wis_saa7113_init); -module_exit(wis_saa7113_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/wis-saa7115.c b/drivers/staging/go7007/wis-saa7115.c deleted file mode 100644 index 46cff59e28b7..000000000000 --- a/drivers/staging/go7007/wis-saa7115.c +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "wis-i2c.h" - -struct wis_saa7115 { - int norm; - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 initial_registers[] = -{ - 0x01, 0x08, - 0x02, 0xc0, - 0x03, 0x20, - 0x04, 0x80, - 0x05, 0x80, - 0x06, 0xeb, - 0x07, 0xe0, - 0x08, 0xf0, /* always toggle FID */ - 0x09, 0x40, - 0x0a, 0x80, - 0x0b, 0x40, - 0x0c, 0x40, - 0x0d, 0x00, - 0x0e, 0x03, - 0x0f, 0x2a, - 0x10, 0x0e, - 0x11, 0x00, - 0x12, 0x8d, - 0x13, 0x00, - 0x14, 0x00, - 0x15, 0x11, - 0x16, 0x01, - 0x17, 0xda, - 0x18, 0x40, - 0x19, 0x80, - 0x1a, 0x00, - 0x1b, 0x42, - 0x1c, 0xa9, - 0x30, 0x66, - 0x31, 0x90, - 0x32, 0x01, - 0x34, 0x00, - 0x35, 0x00, - 0x36, 0x20, - 0x38, 0x03, - 0x39, 0x20, - 0x3a, 0x88, - 0x40, 0x00, - 0x41, 0xff, - 0x42, 0xff, - 0x43, 0xff, - 0x44, 0xff, - 0x45, 0xff, - 0x46, 0xff, - 0x47, 0xff, - 0x48, 0xff, - 0x49, 0xff, - 0x4a, 0xff, - 0x4b, 0xff, - 0x4c, 0xff, - 0x4d, 0xff, - 0x4e, 0xff, - 0x4f, 0xff, - 0x50, 0xff, - 0x51, 0xff, - 0x52, 0xff, - 0x53, 0xff, - 0x54, 0xf4 /*0xff*/, - 0x55, 0xff, - 0x56, 0xff, - 0x57, 0xff, - 0x58, 0x40, - 0x59, 0x47, - 0x5a, 0x06 /*0x03*/, - 0x5b, 0x83, - 0x5d, 0x06, - 0x5e, 0x00, - 0x80, 0x30, /* window defined scaler operation, task A and B enabled */ - 0x81, 0x03, /* use scaler datapath generated V */ - 0x83, 0x00, - 0x84, 0x00, - 0x85, 0x00, - 0x86, 0x45, - 0x87, 0x31, - 0x88, 0xc0, - 0x90, 0x02, /* task A process top field */ - 0x91, 0x08, - 0x92, 0x09, - 0x93, 0x80, - 0x94, 0x06, - 0x95, 0x00, - 0x96, 0xc0, - 0x97, 0x02, - 0x98, 0x12, - 0x99, 0x00, - 0x9a, 0xf2, - 0x9b, 0x00, - 0x9c, 0xd0, - 0x9d, 0x02, - 0x9e, 0xf2, - 0x9f, 0x00, - 0xa0, 0x01, - 0xa1, 0x01, - 0xa2, 0x01, - 0xa4, 0x80, - 0xa5, 0x40, - 0xa6, 0x40, - 0xa8, 0x00, - 0xa9, 0x04, - 0xaa, 0x00, - 0xac, 0x00, - 0xad, 0x02, - 0xae, 0x00, - 0xb0, 0x00, - 0xb1, 0x04, - 0xb2, 0x00, - 0xb3, 0x04, - 0xb4, 0x00, - 0xb8, 0x00, - 0xbc, 0x00, - 0xc0, 0x03, /* task B process bottom field */ - 0xc1, 0x08, - 0xc2, 0x09, - 0xc3, 0x80, - 0xc4, 0x06, - 0xc5, 0x00, - 0xc6, 0xc0, - 0xc7, 0x02, - 0xc8, 0x12, - 0xc9, 0x00, - 0xca, 0xf2, - 0xcb, 0x00, - 0xcc, 0xd0, - 0xcd, 0x02, - 0xce, 0xf2, - 0xcf, 0x00, - 0xd0, 0x01, - 0xd1, 0x01, - 0xd2, 0x01, - 0xd4, 0x80, - 0xd5, 0x40, - 0xd6, 0x40, - 0xd8, 0x00, - 0xd9, 0x04, - 0xda, 0x00, - 0xdc, 0x00, - 0xdd, 0x02, - 0xde, 0x00, - 0xe0, 0x00, - 0xe1, 0x04, - 0xe2, 0x00, - 0xe3, 0x04, - 0xe4, 0x00, - 0xe8, 0x00, - 0x88, 0xf0, /* End of original static list */ - 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0x00; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_saa7115_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_saa7115 *dec = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_S_INPUT: - { - int *input = arg; - - i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input); - i2c_smbus_write_byte_data(client, 0x09, - *input < 6 ? 0x40 : 0xC0); - break; - } - case DECODER_SET_RESOLUTION: - { - struct video_decoder_resolution *res = arg; - /* Course-grained scaler */ - int h_integer_scaler = res->width < 704 ? 704 / res->width : 1; - /* Fine-grained scaler to take care of remainder */ - int h_scaling_increment = (704 / h_integer_scaler) * - 1024 / res->width; - /* Fine-grained scaler only */ - int v_scaling_increment = (dec->norm & V4L2_STD_NTSC ? - 240 : 288) * 1024 / res->height; - u8 regs[] = { - 0x88, 0xc0, - 0x9c, res->width & 0xff, - 0x9d, res->width >> 8, - 0x9e, res->height & 0xff, - 0x9f, res->height >> 8, - 0xa0, h_integer_scaler, - 0xa1, 1, - 0xa2, 1, - 0xa8, h_scaling_increment & 0xff, - 0xa9, h_scaling_increment >> 8, - 0xac, (h_scaling_increment / 2) & 0xff, - 0xad, (h_scaling_increment / 2) >> 8, - 0xb0, v_scaling_increment & 0xff, - 0xb1, v_scaling_increment >> 8, - 0xb2, v_scaling_increment & 0xff, - 0xb3, v_scaling_increment >> 8, - 0xcc, res->width & 0xff, - 0xcd, res->width >> 8, - 0xce, res->height & 0xff, - 0xcf, res->height >> 8, - 0xd0, h_integer_scaler, - 0xd1, 1, - 0xd2, 1, - 0xd8, h_scaling_increment & 0xff, - 0xd9, h_scaling_increment >> 8, - 0xdc, (h_scaling_increment / 2) & 0xff, - 0xdd, (h_scaling_increment / 2) >> 8, - 0xe0, v_scaling_increment & 0xff, - 0xe1, v_scaling_increment >> 8, - 0xe2, v_scaling_increment & 0xff, - 0xe3, v_scaling_increment >> 8, - 0x88, 0xf0, - 0, 0, - }; - write_regs(client, regs); - break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - u8 regs[] = { - 0x88, 0xc0, - 0x98, *input & V4L2_STD_NTSC ? 0x12 : 0x16, - 0x9a, *input & V4L2_STD_NTSC ? 0xf2 : 0x20, - 0x9b, *input & V4L2_STD_NTSC ? 0x00 : 0x01, - 0xc8, *input & V4L2_STD_NTSC ? 0x12 : 0x16, - 0xca, *input & V4L2_STD_NTSC ? 0xf2 : 0x20, - 0xcb, *input & V4L2_STD_NTSC ? 0x00 : 0x01, - 0x88, 0xf0, - 0x30, *input & V4L2_STD_NTSC ? 0x66 : 0x00, - 0x31, *input & V4L2_STD_NTSC ? 0x90 : 0xe0, - 0, 0, - }; - write_regs(client, regs); - dec->norm = *input; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 255) - dec->brightness = 255; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - write_reg(client, 0x0a, dec->brightness); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 127) - dec->contrast = 127; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x0b, dec->contrast); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - write_reg(client, 0x0c, dec->saturation); - break; - case V4L2_CID_HUE: - if (ctrl->value > 127) - dec->hue = 127; - else if (ctrl->value < -128) - dec->hue = -128; - else - dec->hue = ctrl->value; - write_reg(client, 0x0d, dec->hue); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_saa7115_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_saa7115 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->norm = V4L2_STD_NTSC; - dec->brightness = 128; - dec->contrast = 64; - dec->saturation = 64; - dec->hue = 0; - i2c_set_clientdata(client, dec); - - printk(KERN_DEBUG - "wis-saa7115: initializing SAA7115 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR - "wis-saa7115: error initializing SAA7115\n"); - kfree(dec); - return -ENODEV; - } - - return 0; -} - -static int wis_saa7115_remove(struct i2c_client *client) -{ - struct wis_saa7115 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_saa7115_id[] = { - { "wis_saa7115", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_saa7115_id); - -static struct i2c_driver wis_saa7115_driver = { - .driver = { - .name = "WIS SAA7115 I2C driver", - }, - .probe = wis_saa7115_probe, - .remove = wis_saa7115_remove, - .command = wis_saa7115_command, - .id_table = wis_saa7115_id, -}; - -static int __init wis_saa7115_init(void) -{ - return i2c_add_driver(&wis_saa7115_driver); -} - -static void __exit wis_saa7115_cleanup(void) -{ - i2c_del_driver(&wis_saa7115_driver); -} - -module_init(wis_saa7115_init); -module_exit(wis_saa7115_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/wis-sony-tuner.c b/drivers/staging/go7007/wis-sony-tuner.c deleted file mode 100644 index 8f1b7d4f6a2e..000000000000 --- a/drivers/staging/go7007/wis-sony-tuner.c +++ /dev/null @@ -1,720 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "wis-i2c.h" - -/* #define MPX_DEBUG */ - -/* AS(IF/MPX) pin: LOW HIGH/OPEN - * IF/MPX address: 0x42/0x40 0x43/0x44 - */ -#define IF_I2C_ADDR 0x43 -#define MPX_I2C_ADDR 0x44 - -static v4l2_std_id force_band; -static char force_band_str[] = "-"; -module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644); -static int force_mpx_mode = -1; -module_param(force_mpx_mode, int, 0644); - -/* Store tuner info in the same format as tuner.c, so maybe we can put the - * Sony tuner support in there. */ -struct sony_tunertype { - char *name; - unsigned char Vendor; /* unused here */ - unsigned char Type; /* unused here */ - - unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */ - unsigned short thresh2; /* band switch VHF_HI <=> UHF */ - unsigned char VHF_L; - unsigned char VHF_H; - unsigned char UHF; - unsigned char config; - unsigned short IFPCoff; -}; - -/* This array is indexed by (tuner_type - 200) */ -static struct sony_tunertype sony_tuners[] = { - { "Sony PAL+SECAM (BTF-PG472Z)", 0, 0, - 16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623}, - { "Sony NTSC_JP (BTF-PK467Z)", 0, 0, - 16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940}, - { "Sony NTSC (BTF-PB463Z)", 0, 0, - 16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732}, -}; - -struct wis_sony_tuner { - int type; - v4l2_std_id std; - unsigned int freq; - int mpxmode; - u32 audmode; -}; - -/* Basically the same as default_set_tv_freq() in tuner.c */ -static int set_freq(struct i2c_client *client, int freq) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - char *band_name; - int n; - int band_select; - struct sony_tunertype *tun; - u8 buffer[4]; - - tun = &sony_tuners[t->type - 200]; - if (freq < tun->thresh1) { - band_name = "VHF_L"; - band_select = tun->VHF_L; - } else if (freq < tun->thresh2) { - band_name = "VHF_H"; - band_select = tun->VHF_H; - } else { - band_name = "UHF"; - band_select = tun->UHF; - } - printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n", - freq / 16, (freq % 16) * 625, band_name); - n = freq + tun->IFPCoff; - - buffer[0] = n >> 8; - buffer[1] = n & 0xff; - buffer[2] = tun->config; - buffer[3] = band_select; - i2c_master_send(client, buffer, 4); - - return 0; -} - -static int mpx_write(struct i2c_client *client, int dev, int addr, int val) -{ - u8 buffer[5]; - struct i2c_msg msg; - - buffer[0] = dev; - buffer[1] = addr >> 8; - buffer[2] = addr & 0xff; - buffer[3] = val >> 8; - buffer[4] = val & 0xff; - msg.addr = MPX_I2C_ADDR; - msg.flags = 0; - msg.len = 5; - msg.buf = buffer; - i2c_transfer(client->adapter, &msg, 1); - return 0; -} - -/* - * MPX register values for the BTF-PG472Z: - * - * FM_ NICAM_ SCART_ - * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME - * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000 - * --------------------------------------------------------------- - * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500 - * - * B/G - * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500 - * A2 1003 0020 0100 2601 5000 XXXX 0003 7500 - * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500 - * - * I - * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500 - * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500 - * - * D/K - * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500 - * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500 - * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500 - * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500 - * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500 - * - * L/L' - * Mono 0003 0200 0100 7C03 5000 2200 0009 7500 - * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500 - * - * M - * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500 - * - * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX. - * - * Bilingual selection in A2/NICAM: - * - * High byte of SOURCE Left chan Right chan - * 0x01 MAIN SUB - * 0x03 MAIN MAIN - * 0x04 SUB SUB - * - * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or - * 0x00 (all other bands). Force mono in A2 with FMONO_A2: - * - * FMONO_A2 - * 10/0022 - * -------- - * Forced mono ON 07F0 - * Forced mono OFF 0190 - */ - -static struct { - enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode; - u16 modus; - u16 source; - u16 acb; - u16 fm_prescale; - u16 nicam_prescale; - u16 scart_prescale; - u16 system; - u16 volume; -} mpx_audio_modes[] = { - /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0001, 0x7500 }, - /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0003, 0x7500 }, - /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0003, 0x7500 }, - /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0008, 0x7500 }, - /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x7900, 0x0000, 0x000A, 0x7500 }, - /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, - 0x7900, 0x0000, 0x000A, 0x7500 }, - /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, - 0x5000, 0x0000, 0x0004, 0x7500 }, - /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0004, 0x7500 }, - /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0005, 0x7500 }, - /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, - 0x5000, 0x0000, 0x0007, 0x7500 }, - /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, - 0x5000, 0x0000, 0x000B, 0x7500 }, - /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03, - 0x5000, 0x2200, 0x0009, 0x7500 }, - /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03, - 0x5000, 0x0000, 0x0009, 0x7500 }, -}; - -#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes) - -static int mpx_setup(struct i2c_client *client) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - u16 source = 0; - u8 buffer[3]; - struct i2c_msg msg; - - /* reset MPX */ - buffer[0] = 0x00; - buffer[1] = 0x80; - buffer[2] = 0x00; - msg.addr = MPX_I2C_ADDR; - msg.flags = 0; - msg.len = 3; - msg.buf = buffer; - i2c_transfer(client->adapter, &msg, 1); - buffer[1] = 0x00; - i2c_transfer(client->adapter, &msg, 1); - - if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) { - switch (t->audmode) { - case V4L2_TUNER_MODE_MONO: - switch (mpx_audio_modes[t->mpxmode].audio_mode) { - case AUD_A2: - source = mpx_audio_modes[t->mpxmode].source; - break; - case AUD_NICAM: - source = 0x0000; - break; - case AUD_NICAM_L: - source = 0x0200; - break; - default: - break; - } - break; - case V4L2_TUNER_MODE_STEREO: - source = mpx_audio_modes[t->mpxmode].source; - break; - case V4L2_TUNER_MODE_LANG1: - source = 0x0300; - break; - case V4L2_TUNER_MODE_LANG2: - source = 0x0400; - break; - } - source |= mpx_audio_modes[t->mpxmode].source & 0x00ff; - } else - source = mpx_audio_modes[t->mpxmode].source; - - mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus); - mpx_write(client, 0x12, 0x0008, source); - mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb); - mpx_write(client, 0x12, 0x000e, - mpx_audio_modes[t->mpxmode].fm_prescale); - mpx_write(client, 0x12, 0x0010, - mpx_audio_modes[t->mpxmode].nicam_prescale); - mpx_write(client, 0x12, 0x000d, - mpx_audio_modes[t->mpxmode].scart_prescale); - mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system); - mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume); - if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2) - mpx_write(client, 0x10, 0x0022, - t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190); - -#ifdef MPX_DEBUG - { - u8 buf1[3], buf2[2]; - struct i2c_msg msgs[2]; - - printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x " - "%04x %04x %04x %04x %04x %04x\n", - mpx_audio_modes[t->mpxmode].modus, - source, - mpx_audio_modes[t->mpxmode].acb, - mpx_audio_modes[t->mpxmode].fm_prescale, - mpx_audio_modes[t->mpxmode].nicam_prescale, - mpx_audio_modes[t->mpxmode].scart_prescale, - mpx_audio_modes[t->mpxmode].system, - mpx_audio_modes[t->mpxmode].volume); - buf1[0] = 0x11; - buf1[1] = 0x00; - buf1[2] = 0x7e; - msgs[0].addr = MPX_I2C_ADDR; - msgs[0].flags = 0; - msgs[0].len = 3; - msgs[0].buf = buf1; - msgs[1].addr = MPX_I2C_ADDR; - msgs[1].flags = I2C_M_RD; - msgs[1].len = 2; - msgs[1].buf = buf2; - i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n", - buf2[0], buf2[1]); - buf1[0] = 0x11; - buf1[1] = 0x02; - buf1[2] = 0x00; - i2c_transfer(client->adapter, msgs, 2); - printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n", - buf2[0], buf2[1]); - } -#endif - return 0; -} - -/* - * IF configuration values for the BTF-PG472Z: - * - * B/G: 0x94 0x70 0x49 - * I: 0x14 0x70 0x4a - * D/K: 0x14 0x70 0x4b - * L: 0x04 0x70 0x4b - * L': 0x44 0x70 0x53 - * M: 0x50 0x30 0x4c - */ - -static int set_if(struct i2c_client *client) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - u8 buffer[4]; - struct i2c_msg msg; - int default_mpx_mode = 0; - - /* configure IF */ - buffer[0] = 0; - if (t->std & V4L2_STD_PAL_BG) { - buffer[1] = 0x94; - buffer[2] = 0x70; - buffer[3] = 0x49; - default_mpx_mode = 1; - } else if (t->std & V4L2_STD_PAL_I) { - buffer[1] = 0x14; - buffer[2] = 0x70; - buffer[3] = 0x4a; - default_mpx_mode = 4; - } else if (t->std & V4L2_STD_PAL_DK) { - buffer[1] = 0x14; - buffer[2] = 0x70; - buffer[3] = 0x4b; - default_mpx_mode = 6; - } else if (t->std & V4L2_STD_SECAM_L) { - buffer[1] = 0x04; - buffer[2] = 0x70; - buffer[3] = 0x4b; - default_mpx_mode = 11; - } - msg.addr = IF_I2C_ADDR; - msg.flags = 0; - msg.len = 4; - msg.buf = buffer; - i2c_transfer(client->adapter, &msg, 1); - - /* Select MPX mode if not forced by the user */ - if (force_mpx_mode >= 0 && force_mpx_mode < MPX_NUM_MODES) - t->mpxmode = force_mpx_mode; - else - t->mpxmode = default_mpx_mode; - printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n", - t->mpxmode); - mpx_setup(client); - - return 0; -} - -static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - - switch (cmd) { -#if 0 -#ifdef TUNER_SET_TYPE_ADDR - case TUNER_SET_TYPE_ADDR: - { - struct tuner_setup *tun_setup = arg; - int *type = &tun_setup->type; -#else - case TUNER_SET_TYPE: - { - int *type = arg; -#endif - - if (t->type >= 0) { - if (t->type != *type) - printk(KERN_ERR "wis-sony-tuner: type already " - "set to %d, ignoring request for %d\n", - t->type, *type); - break; - } - t->type = *type; - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - switch (force_band_str[0]) { - case 'b': - case 'B': - case 'g': - case 'G': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-B/G bands\n"); - force_band = V4L2_STD_PAL_BG; - break; - case 'i': - case 'I': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-I band\n"); - force_band = V4L2_STD_PAL_I; - break; - case 'd': - case 'D': - case 'k': - case 'K': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to PAL-D/K bands\n"); - force_band = V4L2_STD_PAL_I; - break; - case 'l': - case 'L': - printk(KERN_INFO "wis-sony-tuner: forcing " - "tuner to SECAM-L band\n"); - force_band = V4L2_STD_SECAM_L; - break; - default: - force_band = 0; - break; - } - if (force_band) - t->std = force_band; - else - t->std = V4L2_STD_PAL_BG; - set_if(client); - break; - case TUNER_SONY_BTF_PK467Z: - t->std = V4L2_STD_NTSC_M_JP; - break; - case TUNER_SONY_BTF_PB463Z: - t->std = V4L2_STD_NTSC_M; - break; - default: - printk(KERN_ERR "wis-sony-tuner: tuner type %d is not " - "supported by this module\n", *type); - break; - } - if (type >= 0) - printk(KERN_INFO - "wis-sony-tuner: type set to %d (%s)\n", - t->type, sony_tuners[t->type - 200].name); - break; - } -#endif - case VIDIOC_G_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - f->frequency = t->freq; - break; - } - case VIDIOC_S_FREQUENCY: - { - struct v4l2_frequency *f = arg; - - t->freq = f->frequency; - set_freq(client, t->freq); - break; - } - case VIDIOC_ENUMSTD: - { - struct v4l2_standard *std = arg; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - switch (std->index) { - case 0: - v4l2_video_std_construct(std, - V4L2_STD_PAL_BG, "PAL-B/G"); - break; - case 1: - v4l2_video_std_construct(std, - V4L2_STD_PAL_I, "PAL-I"); - break; - case 2: - v4l2_video_std_construct(std, - V4L2_STD_PAL_DK, "PAL-D/K"); - break; - case 3: - v4l2_video_std_construct(std, - V4L2_STD_SECAM_L, "SECAM-L"); - break; - default: - std->id = 0; /* hack to indicate EINVAL */ - break; - } - break; - case TUNER_SONY_BTF_PK467Z: - if (std->index != 0) { - std->id = 0; /* hack to indicate EINVAL */ - break; - } - v4l2_video_std_construct(std, - V4L2_STD_NTSC_M_JP, "NTSC-J"); - break; - case TUNER_SONY_BTF_PB463Z: - if (std->index != 0) { - std->id = 0; /* hack to indicate EINVAL */ - break; - } - v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC"); - break; - } - break; - } - case VIDIOC_G_STD: - { - v4l2_std_id *std = arg; - - *std = t->std; - break; - } - case VIDIOC_S_STD: - { - v4l2_std_id *std = arg; - v4l2_std_id old = t->std; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - if (force_band && (*std & force_band) != *std && - *std != V4L2_STD_PAL && - *std != V4L2_STD_SECAM) { - printk(KERN_DEBUG "wis-sony-tuner: ignoring " - "requested TV standard in " - "favor of force_band value\n"); - t->std = force_band; - } else if (*std & V4L2_STD_PAL_BG) { /* default */ - t->std = V4L2_STD_PAL_BG; - } else if (*std & V4L2_STD_PAL_I) { - t->std = V4L2_STD_PAL_I; - } else if (*std & V4L2_STD_PAL_DK) { - t->std = V4L2_STD_PAL_DK; - } else if (*std & V4L2_STD_SECAM_L) { - t->std = V4L2_STD_SECAM_L; - } else { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); - *std = 0; /* hack to indicate EINVAL */ - break; - } - if (old != t->std) - set_if(client); - break; - case TUNER_SONY_BTF_PK467Z: - if (!(*std & V4L2_STD_NTSC_M_JP)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); - *std = 0; /* hack to indicate EINVAL */ - } - break; - case TUNER_SONY_BTF_PB463Z: - if (!(*std & V4L2_STD_NTSC_M)) { - printk(KERN_ERR "wis-sony-tuner: TV standard " - "not supported\n"); - *std = 0; /* hack to indicate EINVAL */ - } - break; - } - break; - } - case VIDIOC_QUERYSTD: - { - v4l2_std_id *std = arg; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - if (force_band) - *std = force_band; - else - *std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I | - V4L2_STD_PAL_DK | V4L2_STD_SECAM_L; - break; - case TUNER_SONY_BTF_PK467Z: - *std = V4L2_STD_NTSC_M_JP; - break; - case TUNER_SONY_BTF_PB463Z: - *std = V4L2_STD_NTSC_M; - break; - } - break; - } - case VIDIOC_G_TUNER: - { - struct v4l2_tuner *tun = arg; - - memset(tun, 0, sizeof(*tun)); - strcpy(tun->name, "Television"); - tun->type = V4L2_TUNER_ANALOG_TV; - tun->rangelow = 0UL; /* does anything use these? */ - tun->rangehigh = 0xffffffffUL; - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - tun->capability = V4L2_TUNER_CAP_NORM | - V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | - V4L2_TUNER_CAP_LANG2; - tun->rxsubchans = V4L2_TUNER_SUB_MONO | - V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 | - V4L2_TUNER_SUB_LANG2; - break; - case TUNER_SONY_BTF_PK467Z: - case TUNER_SONY_BTF_PB463Z: - tun->capability = V4L2_TUNER_CAP_STEREO; - tun->rxsubchans = V4L2_TUNER_SUB_MONO | - V4L2_TUNER_SUB_STEREO; - break; - } - tun->audmode = t->audmode; - return 0; - } - case VIDIOC_S_TUNER: - { - struct v4l2_tuner *tun = arg; - - switch (t->type) { - case TUNER_SONY_BTF_PG472Z: - if (tun->audmode != t->audmode) { - t->audmode = tun->audmode; - mpx_setup(client); - } - break; - case TUNER_SONY_BTF_PK467Z: - case TUNER_SONY_BTF_PB463Z: - break; - } - return 0; - } - default: - break; - } - return 0; -} - -static int wis_sony_tuner_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_sony_tuner *t; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) - return -ENODEV; - - t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL); - if (t == NULL) - return -ENOMEM; - - t->type = -1; - t->freq = 0; - t->mpxmode = 0; - t->audmode = V4L2_TUNER_MODE_STEREO; - i2c_set_clientdata(client, t); - - printk(KERN_DEBUG - "wis-sony-tuner: initializing tuner at address %d on %s\n", - client->addr, adapter->name); - - return 0; -} - -static int wis_sony_tuner_remove(struct i2c_client *client) -{ - struct wis_sony_tuner *t = i2c_get_clientdata(client); - - kfree(t); - return 0; -} - -static const struct i2c_device_id wis_sony_tuner_id[] = { - { "wis_sony_tuner", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_sony_tuner_id); - -static struct i2c_driver wis_sony_tuner_driver = { - .driver = { - .name = "WIS Sony TV Tuner I2C driver", - }, - .probe = wis_sony_tuner_probe, - .remove = wis_sony_tuner_remove, - .command = tuner_command, - .id_table = wis_sony_tuner_id, -}; - -static int __init wis_sony_tuner_init(void) -{ - return i2c_add_driver(&wis_sony_tuner_driver); -} - -static void __exit wis_sony_tuner_cleanup(void) -{ - i2c_del_driver(&wis_sony_tuner_driver); -} - -module_init(wis_sony_tuner_init); -module_exit(wis_sony_tuner_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/wis-tw2804.c b/drivers/staging/go7007/wis-tw2804.c deleted file mode 100644 index 9134f03e3cf0..000000000000 --- a/drivers/staging/go7007/wis-tw2804.c +++ /dev/null @@ -1,357 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "wis-i2c.h" - -struct wis_tw2804 { - int channel; - int norm; - int brightness; - int contrast; - int saturation; - int hue; -}; - -static u8 global_registers[] = { - 0x39, 0x00, - 0x3a, 0xff, - 0x3b, 0x84, - 0x3c, 0x80, - 0x3d, 0x80, - 0x3e, 0x82, - 0x3f, 0x82, - 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ -}; - -static u8 channel_registers[] = { - 0x01, 0xc4, - 0x02, 0xa5, - 0x03, 0x20, - 0x04, 0xd0, - 0x05, 0x20, - 0x06, 0xd0, - 0x07, 0x88, - 0x08, 0x20, - 0x09, 0x07, - 0x0a, 0xf0, - 0x0b, 0x07, - 0x0c, 0xf0, - 0x0d, 0x40, - 0x0e, 0xd2, - 0x0f, 0x80, - 0x10, 0x80, - 0x11, 0x80, - 0x12, 0x80, - 0x13, 0x1f, - 0x14, 0x00, - 0x15, 0x00, - 0x16, 0x00, - 0x17, 0x00, - 0x18, 0xff, - 0x19, 0xff, - 0x1a, 0xff, - 0x1b, 0xff, - 0x1c, 0xff, - 0x1d, 0xff, - 0x1e, 0xff, - 0x1f, 0xff, - 0x20, 0x07, - 0x21, 0x07, - 0x22, 0x00, - 0x23, 0x91, - 0x24, 0x51, - 0x25, 0x03, - 0x26, 0x00, - 0x27, 0x00, - 0x28, 0x00, - 0x29, 0x00, - 0x2a, 0x00, - 0x2b, 0x00, - 0x2c, 0x00, - 0x2d, 0x00, - 0x2e, 0x00, - 0x2f, 0x00, - 0x30, 0x00, - 0x31, 0x00, - 0x32, 0x00, - 0x33, 0x00, - 0x34, 0x00, - 0x35, 0x00, - 0x36, 0x00, - 0x37, 0x00, - 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel) -{ - return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); -} - -static int write_regs(struct i2c_client *client, u8 *regs, int channel) -{ - int i; - - for (i = 0; regs[i] != 0xff; i += 2) - if (i2c_smbus_write_byte_data(client, - regs[i] | (channel << 6), regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_tw2804_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_tw2804 *dec = i2c_get_clientdata(client); - - if (cmd == DECODER_SET_CHANNEL) { - int *input = arg; - - if (*input < 0 || *input > 3) { - printk(KERN_ERR "wis-tw2804: channel %d is not " - "between 0 and 3!\n", *input); - return 0; - } - dec->channel = *input; - printk(KERN_DEBUG "wis-tw2804: initializing TW2804 " - "channel %d\n", dec->channel); - if (dec->channel == 0 && - write_regs(client, global_registers, 0) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 global registers\n"); - return 0; - } - if (write_regs(client, channel_registers, dec->channel) < 0) { - printk(KERN_ERR "wis-tw2804: error initializing " - "TW2804 channel %d\n", dec->channel); - return 0; - } - return 0; - } - - if (dec->channel < 0) { - printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until " - "channel number is set\n", cmd); - return 0; - } - - switch (cmd) { - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - u8 regs[] = { - 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84, - 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04, - 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a, - 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40, - 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40, - 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, - 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, - 0xff, 0xff, - }; - write_regs(client, regs, dec->channel); - dec->norm = *input; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 128; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 255) - dec->brightness = 255; - else if (ctrl->value < 0) - dec->brightness = 0; - else - dec->brightness = ctrl->value; - write_reg(client, 0x12, dec->brightness, dec->channel); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 255) - dec->contrast = 255; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x11, dec->contrast, dec->channel); - break; - case V4L2_CID_SATURATION: - if (ctrl->value > 255) - dec->saturation = 255; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - write_reg(client, 0x10, dec->saturation, dec->channel); - break; - case V4L2_CID_HUE: - if (ctrl->value > 255) - dec->hue = 255; - else if (ctrl->value < 0) - dec->hue = 0; - else - dec->hue = ctrl->value; - write_reg(client, 0x0f, dec->hue, dec->channel); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_tw2804_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_tw2804 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->channel = -1; - dec->norm = V4L2_STD_NTSC; - dec->brightness = 128; - dec->contrast = 128; - dec->saturation = 128; - dec->hue = 128; - i2c_set_clientdata(client, dec); - - printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", - client->addr, adapter->name); - - return 0; -} - -static int wis_tw2804_remove(struct i2c_client *client) -{ - struct wis_tw2804 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_tw2804_id[] = { - { "wis_tw2804", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_tw2804_id); - -static struct i2c_driver wis_tw2804_driver = { - .driver = { - .name = "WIS TW2804 I2C driver", - }, - .probe = wis_tw2804_probe, - .remove = wis_tw2804_remove, - .command = wis_tw2804_command, - .id_table = wis_tw2804_id, -}; - -static int __init wis_tw2804_init(void) -{ - return i2c_add_driver(&wis_tw2804_driver); -} - -static void __exit wis_tw2804_cleanup(void) -{ - i2c_del_driver(&wis_tw2804_driver); -} - -module_init(wis_tw2804_init); -module_exit(wis_tw2804_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/wis-tw9903.c b/drivers/staging/go7007/wis-tw9903.c deleted file mode 100644 index 9230f4a80529..000000000000 --- a/drivers/staging/go7007/wis-tw9903.c +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "wis-i2c.h" - -struct wis_tw9903 { - int norm; - int brightness; - int contrast; - int hue; -}; - -static u8 initial_registers[] = -{ - 0x02, 0x44, /* input 1, composite */ - 0x03, 0x92, /* correct digital format */ - 0x04, 0x00, - 0x05, 0x80, /* or 0x00 for PAL */ - 0x06, 0x40, /* second internal current reference */ - 0x07, 0x02, /* window */ - 0x08, 0x14, /* window */ - 0x09, 0xf0, /* window */ - 0x0a, 0x81, /* window */ - 0x0b, 0xd0, /* window */ - 0x0c, 0x8c, - 0x0d, 0x00, /* scaling */ - 0x0e, 0x11, /* scaling */ - 0x0f, 0x00, /* scaling */ - 0x10, 0x00, /* brightness */ - 0x11, 0x60, /* contrast */ - 0x12, 0x01, /* sharpness */ - 0x13, 0x7f, /* U gain */ - 0x14, 0x5a, /* V gain */ - 0x15, 0x00, /* hue */ - 0x16, 0xc3, /* sharpness */ - 0x18, 0x00, - 0x19, 0x58, /* vbi */ - 0x1a, 0x80, - 0x1c, 0x0f, /* video norm */ - 0x1d, 0x7f, /* video norm */ - 0x20, 0xa0, /* clamping gain (working 0x50) */ - 0x21, 0x22, - 0x22, 0xf0, - 0x23, 0xfe, - 0x24, 0x3c, - 0x25, 0x38, - 0x26, 0x44, - 0x27, 0x20, - 0x28, 0x00, - 0x29, 0x15, - 0x2a, 0xa0, - 0x2b, 0x44, - 0x2c, 0x37, - 0x2d, 0x00, - 0x2e, 0xa5, /* burst PLL control (working: a9) */ - 0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */ - 0x31, 0x00, - 0x33, 0x22, - 0x34, 0x11, - 0x35, 0x35, - 0x3b, 0x05, - 0x06, 0xc0, /* reset device */ - 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ -}; - -static int write_reg(struct i2c_client *client, u8 reg, u8 value) -{ - return i2c_smbus_write_byte_data(client, reg, value); -} - -static int write_regs(struct i2c_client *client, u8 *regs) -{ - int i; - - for (i = 0; regs[i] != 0x00; i += 2) - if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) - return -1; - return 0; -} - -static int wis_tw9903_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - struct wis_tw9903 *dec = i2c_get_clientdata(client); - - switch (cmd) { - case VIDIOC_S_INPUT: - { - int *input = arg; - - i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1)); - break; - } -#if 0 - /* The scaler on this thing seems to be horribly broken */ - case DECODER_SET_RESOLUTION: - { - struct video_decoder_resolution *res = arg; - /*int hscale = 256 * 720 / res->width;*/ - int hscale = 256 * 720 / (res->width - (res->width > 704 ? 0 : 8)); - int vscale = 256 * (dec->norm & V4L2_STD_NTSC ? 240 : 288) - / res->height; - u8 regs[] = { - 0x0d, vscale & 0xff, - 0x0f, hscale & 0xff, - 0x0e, ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8), - 0x06, 0xc0, /* reset device */ - 0, 0, - }; - printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n", - vscale, hscale); - /*write_regs(client, regs);*/ - break; - } -#endif - case VIDIOC_S_STD: - { - v4l2_std_id *input = arg; - u8 regs[] = { - 0x05, *input & V4L2_STD_NTSC ? 0x80 : 0x00, - 0x07, *input & V4L2_STD_NTSC ? 0x02 : 0x12, - 0x08, *input & V4L2_STD_NTSC ? 0x14 : 0x18, - 0x09, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, - 0, 0, - }; - write_regs(client, regs); - dec->norm = *input; - break; - } - case VIDIOC_QUERYCTRL: - { - struct v4l2_queryctrl *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0x00; - ctrl->flags = 0; - break; - case V4L2_CID_CONTRAST: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 255; - ctrl->step = 1; - ctrl->default_value = 0x60; - ctrl->flags = 0; - break; -#if 0 - /* I don't understand how the Chroma Gain registers work... */ - case V4L2_CID_SATURATION: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); - ctrl->minimum = 0; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 64; - ctrl->flags = 0; - break; -#endif - case V4L2_CID_HUE: - ctrl->type = V4L2_CTRL_TYPE_INTEGER; - strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); - ctrl->minimum = -128; - ctrl->maximum = 127; - ctrl->step = 1; - ctrl->default_value = 0; - ctrl->flags = 0; - break; - } - break; - } - case VIDIOC_S_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - if (ctrl->value > 127) - dec->brightness = 127; - else if (ctrl->value < -128) - dec->brightness = -128; - else - dec->brightness = ctrl->value; - write_reg(client, 0x10, dec->brightness); - break; - case V4L2_CID_CONTRAST: - if (ctrl->value > 255) - dec->contrast = 255; - else if (ctrl->value < 0) - dec->contrast = 0; - else - dec->contrast = ctrl->value; - write_reg(client, 0x11, dec->contrast); - break; -#if 0 - case V4L2_CID_SATURATION: - if (ctrl->value > 127) - dec->saturation = 127; - else if (ctrl->value < 0) - dec->saturation = 0; - else - dec->saturation = ctrl->value; - /*write_reg(client, 0x0c, dec->saturation);*/ - break; -#endif - case V4L2_CID_HUE: - if (ctrl->value > 127) - dec->hue = 127; - else if (ctrl->value < -128) - dec->hue = -128; - else - dec->hue = ctrl->value; - write_reg(client, 0x15, dec->hue); - break; - } - break; - } - case VIDIOC_G_CTRL: - { - struct v4l2_control *ctrl = arg; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = dec->brightness; - break; - case V4L2_CID_CONTRAST: - ctrl->value = dec->contrast; - break; -#if 0 - case V4L2_CID_SATURATION: - ctrl->value = dec->saturation; - break; -#endif - case V4L2_CID_HUE: - ctrl->value = dec->hue; - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_tw9903_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - struct wis_tw9903 *dec; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) - return -ENODEV; - - dec = kmalloc(sizeof(struct wis_tw9903), GFP_KERNEL); - if (dec == NULL) - return -ENOMEM; - - dec->norm = V4L2_STD_NTSC; - dec->brightness = 0; - dec->contrast = 0x60; - dec->hue = 0; - i2c_set_clientdata(client, dec); - - printk(KERN_DEBUG - "wis-tw9903: initializing TW9903 at address %d on %s\n", - client->addr, adapter->name); - - if (write_regs(client, initial_registers) < 0) { - printk(KERN_ERR "wis-tw9903: error initializing TW9903\n"); - kfree(dec); - return -ENODEV; - } - - return 0; -} - -static int wis_tw9903_remove(struct i2c_client *client) -{ - struct wis_tw9903 *dec = i2c_get_clientdata(client); - - kfree(dec); - return 0; -} - -static const struct i2c_device_id wis_tw9903_id[] = { - { "wis_tw9903", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_tw9903_id); - -static struct i2c_driver wis_tw9903_driver = { - .driver = { - .name = "WIS TW9903 I2C driver", - }, - .probe = wis_tw9903_probe, - .remove = wis_tw9903_remove, - .command = wis_tw9903_command, - .id_table = wis_tw9903_id, -}; - -static int __init wis_tw9903_init(void) -{ - return i2c_add_driver(&wis_tw9903_driver); -} - -static void __exit wis_tw9903_cleanup(void) -{ - i2c_del_driver(&wis_tw9903_driver); -} - -module_init(wis_tw9903_init); -module_exit(wis_tw9903_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/go7007/wis-uda1342.c b/drivers/staging/go7007/wis-uda1342.c deleted file mode 100644 index 0127be2f3be0..000000000000 --- a/drivers/staging/go7007/wis-uda1342.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. - */ - -#include -#include -#include -#include -#include -#include - -#include "wis-i2c.h" - -static int write_reg(struct i2c_client *client, int reg, int value) -{ - /* UDA1342 wants MSB first, but SMBus sends LSB first */ - i2c_smbus_write_word_data(client, reg, swab16(value)); - return 0; -} - -static int wis_uda1342_command(struct i2c_client *client, - unsigned int cmd, void *arg) -{ - switch (cmd) { - case VIDIOC_S_AUDIO: - { - int *inp = arg; - - switch (*inp) { - case TVAUDIO_INPUT_TUNER: - write_reg(client, 0x00, 0x1441); /* select input 2 */ - break; - case TVAUDIO_INPUT_EXTERN: - write_reg(client, 0x00, 0x1241); /* select input 1 */ - break; - default: - printk(KERN_ERR "wis-uda1342: input %d not supported\n", - *inp); - break; - } - break; - } - default: - break; - } - return 0; -} - -static int wis_uda1342_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct i2c_adapter *adapter = client->adapter; - - if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) - return -ENODEV; - - printk(KERN_DEBUG - "wis-uda1342: initializing UDA1342 at address %d on %s\n", - client->addr, adapter->name); - - write_reg(client, 0x00, 0x8000); /* reset registers */ - write_reg(client, 0x00, 0x1241); /* select input 1 */ - - return 0; -} - -static int wis_uda1342_remove(struct i2c_client *client) -{ - return 0; -} - -static const struct i2c_device_id wis_uda1342_id[] = { - { "wis_uda1342", 0 }, - { } -}; -MODULE_DEVICE_TABLE(i2c, wis_uda1342_id); - -static struct i2c_driver wis_uda1342_driver = { - .driver = { - .name = "WIS UDA1342 I2C driver", - }, - .probe = wis_uda1342_probe, - .remove = wis_uda1342_remove, - .command = wis_uda1342_command, - .id_table = wis_uda1342_id, -}; - -static int __init wis_uda1342_init(void) -{ - return i2c_add_driver(&wis_uda1342_driver); -} - -static void __exit wis_uda1342_cleanup(void) -{ - i2c_del_driver(&wis_uda1342_driver); -} - -module_init(wis_uda1342_init); -module_exit(wis_uda1342_cleanup); - -MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/lirc/Kconfig b/drivers/staging/lirc/Kconfig deleted file mode 100644 index 526ec0fc2f04..000000000000 --- a/drivers/staging/lirc/Kconfig +++ /dev/null @@ -1,78 +0,0 @@ -# -# LIRC driver(s) configuration -# -menuconfig LIRC_STAGING - bool "Linux Infrared Remote Control IR receiver/transmitter drivers" - depends on LIRC - help - Say Y here, and all supported Linux Infrared Remote Control IR and - RF receiver and transmitter drivers will be displayed. When paired - with a remote control and the lirc daemon, the receiver drivers - allow control of your Linux system via remote control. - -if LIRC_STAGING - -config LIRC_BT829 - tristate "BT829 based hardware" - depends on LIRC && PCI - help - Driver for the IR interface on BT829-based hardware - -config LIRC_IGORPLUGUSB - tristate "Igor Cesko's USB IR Receiver" - depends on LIRC && USB - help - Driver for Igor Cesko's USB IR Receiver - -config LIRC_IMON - tristate "Legacy SoundGraph iMON Receiver and Display" - depends on LIRC && USB - help - Driver for the original SoundGraph iMON IR Receiver and Display - - Current generation iMON devices use the input layer imon driver. - -config LIRC_PARALLEL - tristate "Homebrew Parallel Port Receiver" - depends on LIRC && PARPORT - help - Driver for Homebrew Parallel Port Receivers - -config LIRC_SASEM - tristate "Sasem USB IR Remote" - depends on LIRC && USB - help - Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module - -config LIRC_SERIAL - tristate "Homebrew Serial Port Receiver" - depends on LIRC - help - Driver for Homebrew Serial Port Receivers - -config LIRC_SERIAL_TRANSMITTER - bool "Serial Port Transmitter" - default y - depends on LIRC_SERIAL - help - Serial Port Transmitter support - -config LIRC_SIR - tristate "Built-in SIR IrDA port" - depends on LIRC - help - Driver for the SIR IrDA port - -config LIRC_TTUSBIR - tristate "Technotrend USB IR Receiver" - depends on LIRC && USB - help - Driver for the Technotrend USB IR Receiver - -config LIRC_ZILOG - tristate "Zilog/Hauppauge IR Transmitter" - depends on LIRC && I2C - help - Driver for the Zilog/Hauppauge IR Transmitter, found on - PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards -endif diff --git a/drivers/staging/lirc/Makefile b/drivers/staging/lirc/Makefile deleted file mode 100644 index d76b0fa2af53..000000000000 --- a/drivers/staging/lirc/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Makefile for the lirc drivers. -# - -# Each configuration option enables a list of files. - -obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o -obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o -obj-$(CONFIG_LIRC_IMON) += lirc_imon.o -obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o -obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o -obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o -obj-$(CONFIG_LIRC_SIR) += lirc_sir.o -obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o -obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o diff --git a/drivers/staging/lirc/TODO b/drivers/staging/lirc/TODO deleted file mode 100644 index b6cb593f55c6..000000000000 --- a/drivers/staging/lirc/TODO +++ /dev/null @@ -1,8 +0,0 @@ -- All drivers should either be ported to ir-core, or dropped entirely - (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an - example of a previously completed port). - -Please send patches to: -Jarod Wilson -Greg Kroah-Hartman - diff --git a/drivers/staging/lirc/TODO.lirc_zilog b/drivers/staging/lirc/TODO.lirc_zilog deleted file mode 100644 index a97800a8e127..000000000000 --- a/drivers/staging/lirc/TODO.lirc_zilog +++ /dev/null @@ -1,36 +0,0 @@ -1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for -the chips supported by lirc_zilog. Before moving lirc_zilog out of staging: - -a. ir-kbd-i2c needs a module parameter added to allow the user to tell - ir-kbd-i2c to ignore Z8 IR units. - -b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c - does. - - -2. lirc_zilog module ref-counting need examination. It has not been -verified that cdev and lirc_dev will take the proper module references on -lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node -is open. - -(The good news is ref-counting of lirc_zilog internal structures appears to be -complete. Testing has shown the cx18 module can be unloaded out from under -irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse -effects. The cx18 module could then be reloaded and irw properly began -receiving button presses again and ir_send worked without error.) - - -3. Bridge drivers, if able, should provide a chip reset() callback -to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines -to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog -to bring the chip back to normal when it hangs, in the same places the -original lirc_pvr150 driver code does. This is not strictly needed, so it -is not required to move lirc_zilog out of staging. - -Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed -and installed on Hauppauge products. When working on either module, developers -must consider at least the following bridge drivers which mention an IR Rx unit -at address 0x71 (indicative of a Z8): - - ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134 - diff --git a/drivers/staging/lirc/lirc_bt829.c b/drivers/staging/lirc/lirc_bt829.c deleted file mode 100644 index c5a0d27a02dc..000000000000 --- a/drivers/staging/lirc/lirc_bt829.c +++ /dev/null @@ -1,383 +0,0 @@ -/* - * Remote control driver for the TV-card based on bt829 - * - * by Leonid Froenchenko - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 - -static int poll_main(void); -static int atir_init_start(void); - -static void write_index(unsigned char index, unsigned int value); -static unsigned int read_index(unsigned char index); - -static void do_i2c_start(void); -static void do_i2c_stop(void); - -static void seems_wr_byte(unsigned char al); -static unsigned char seems_rd_byte(void); - -static unsigned int read_index(unsigned char al); -static void write_index(unsigned char ah, unsigned int edx); - -static void cycle_delay(int cycle); - -static void do_set_bits(unsigned char bl); -static unsigned char do_get_bits(void); - -#define DATA_PCI_OFF 0x7FFC00 -#define WAIT_CYCLE 20 - -#define DRIVER_NAME "lirc_bt829" - -static int debug; -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \ - } while (0) - -static int atir_minor; -static unsigned long pci_addr_phys; -static unsigned char *pci_addr_lin; - -static struct lirc_driver atir_driver; - -static struct pci_dev *do_pci_probe(void) -{ - struct pci_dev *my_dev; - my_dev = pci_get_device(PCI_VENDOR_ID_ATI, - PCI_DEVICE_ID_ATI_264VT, NULL); - if (my_dev) { - printk(KERN_ERR DRIVER_NAME ": Using device: %s\n", - pci_name(my_dev)); - pci_addr_phys = 0; - if (my_dev->resource[0].flags & IORESOURCE_MEM) { - pci_addr_phys = my_dev->resource[0].start; - printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n", - (unsigned int)pci_addr_phys); - } - if (pci_addr_phys == 0) { - printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n"); - return NULL; - } - } else { - printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n"); - return NULL; - } - return my_dev; -} - -static int atir_add_to_buf(void *data, struct lirc_buffer *buf) -{ - unsigned char key; - int status; - status = poll_main(); - key = (status >> 8) & 0xFF; - if (status & 0xFF) { - dprintk("reading key %02X\n", key); - lirc_buffer_write(buf, &key); - return 0; - } - return -ENODATA; -} - -static int atir_set_use_inc(void *data) -{ - dprintk("driver is opened\n"); - return 0; -} - -static void atir_set_use_dec(void *data) -{ - dprintk("driver is closed\n"); -} - -int init_module(void) -{ - struct pci_dev *pdev; - - pdev = do_pci_probe(); - if (pdev == NULL) - return -ENODEV; - - if (!atir_init_start()) - return -ENODEV; - - strcpy(atir_driver.name, "ATIR"); - atir_driver.minor = -1; - atir_driver.code_length = 8; - atir_driver.sample_rate = 10; - atir_driver.data = 0; - atir_driver.add_to_buf = atir_add_to_buf; - atir_driver.set_use_inc = atir_set_use_inc; - atir_driver.set_use_dec = atir_set_use_dec; - atir_driver.dev = &pdev->dev; - atir_driver.owner = THIS_MODULE; - - atir_minor = lirc_register_driver(&atir_driver); - if (atir_minor < 0) { - printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n"); - return atir_minor; - } - dprintk("driver is registered on minor %d\n", atir_minor); - - return 0; -} - - -void cleanup_module(void) -{ - lirc_unregister_driver(atir_minor); -} - - -static int atir_init_start(void) -{ - pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); - if (pci_addr_lin == 0) { - printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n"); - return 0; - } - return 1; -} - -static void cycle_delay(int cycle) -{ - udelay(WAIT_CYCLE*cycle); -} - - -static int poll_main() -{ - unsigned char status_high, status_low; - - do_i2c_start(); - - seems_wr_byte(0xAA); - seems_wr_byte(0x01); - - do_i2c_start(); - - seems_wr_byte(0xAB); - - status_low = seems_rd_byte(); - status_high = seems_rd_byte(); - - do_i2c_stop(); - - return (status_high << 8) | status_low; -} - -static void do_i2c_start(void) -{ - do_set_bits(3); - cycle_delay(4); - - do_set_bits(1); - cycle_delay(7); - - do_set_bits(0); - cycle_delay(2); -} - -static void do_i2c_stop(void) -{ - unsigned char bits; - bits = do_get_bits() & 0xFD; - do_set_bits(bits); - cycle_delay(1); - - bits |= 1; - do_set_bits(bits); - cycle_delay(2); - - bits |= 2; - do_set_bits(bits); - bits = 3; - do_set_bits(bits); - cycle_delay(2); -} - -static void seems_wr_byte(unsigned char value) -{ - int i; - unsigned char reg; - - reg = do_get_bits(); - for (i = 0; i < 8; i++) { - if (value & 0x80) - reg |= 0x02; - else - reg &= 0xFD; - - do_set_bits(reg); - cycle_delay(1); - - reg |= 1; - do_set_bits(reg); - cycle_delay(1); - - reg &= 0xFE; - do_set_bits(reg); - cycle_delay(1); - value <<= 1; - } - cycle_delay(2); - - reg |= 2; - do_set_bits(reg); - - reg |= 1; - do_set_bits(reg); - - cycle_delay(1); - do_get_bits(); - - reg &= 0xFE; - do_set_bits(reg); - cycle_delay(3); -} - -static unsigned char seems_rd_byte(void) -{ - int i; - int rd_byte; - unsigned char bits_2, bits_1; - - bits_1 = do_get_bits() | 2; - do_set_bits(bits_1); - - rd_byte = 0; - for (i = 0; i < 8; i++) { - bits_1 &= 0xFE; - do_set_bits(bits_1); - cycle_delay(2); - - bits_1 |= 1; - do_set_bits(bits_1); - cycle_delay(1); - - bits_2 = do_get_bits(); - if (bits_2 & 2) - rd_byte |= 1; - - rd_byte <<= 1; - } - - bits_1 = 0; - if (bits_2 == 0) - bits_1 |= 2; - - do_set_bits(bits_1); - cycle_delay(2); - - bits_1 |= 1; - do_set_bits(bits_1); - cycle_delay(3); - - bits_1 &= 0xFE; - do_set_bits(bits_1); - cycle_delay(2); - - rd_byte >>= 1; - rd_byte &= 0xFF; - return rd_byte; -} - -static void do_set_bits(unsigned char new_bits) -{ - int reg_val; - reg_val = read_index(0x34); - if (new_bits & 2) { - reg_val &= 0xFFFFFFDF; - reg_val |= 1; - } else { - reg_val &= 0xFFFFFFFE; - reg_val |= 0x20; - } - reg_val |= 0x10; - write_index(0x34, reg_val); - - reg_val = read_index(0x31); - if (new_bits & 1) - reg_val |= 0x1000000; - else - reg_val &= 0xFEFFFFFF; - - reg_val |= 0x8000000; - write_index(0x31, reg_val); -} - -static unsigned char do_get_bits(void) -{ - unsigned char bits; - int reg_val; - - reg_val = read_index(0x34); - reg_val |= 0x10; - reg_val &= 0xFFFFFFDF; - write_index(0x34, reg_val); - - reg_val = read_index(0x34); - bits = 0; - if (reg_val & 8) - bits |= 2; - else - bits &= 0xFD; - - reg_val = read_index(0x31); - if (reg_val & 0x1000000) - bits |= 1; - else - bits &= 0xFE; - - return bits; -} - -static unsigned int read_index(unsigned char index) -{ - unsigned char *addr; - unsigned int value; - /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */ - addr = pci_addr_lin + ((index & 0xFF) << 2); - value = readl(addr); - return value; -} - -static void write_index(unsigned char index, unsigned int reg_val) -{ - unsigned char *addr; - addr = pci_addr_lin + ((index & 0xFF) << 2); - writel(reg_val, addr); -} - -MODULE_AUTHOR("Froenchenko Leonid"); -MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards"); -MODULE_LICENSE("GPL"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/staging/lirc/lirc_ene0100.h b/drivers/staging/lirc/lirc_ene0100.h deleted file mode 100644 index 06bebd6acc46..000000000000 --- a/drivers/staging/lirc/lirc_ene0100.h +++ /dev/null @@ -1,169 +0,0 @@ -/* - * driver for ENE KB3926 B/C/D CIR (also known as ENE0100) - * - * Copyright (C) 2009 Maxim Levitsky - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 - -/* hardware address */ -#define ENE_STATUS 0 /* hardware status - unused */ -#define ENE_ADDR_HI 1 /* hi byte of register address */ -#define ENE_ADDR_LO 2 /* low byte of register address */ -#define ENE_IO 3 /* read/write window */ -#define ENE_MAX_IO 4 - -/* 8 bytes of samples, divided in 2 halfs*/ -#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */ -#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */ -#define ENE_SAMPLE_VALUE_MASK 0x7F -#define ENE_SAMPLE_OVERFLOW 0x7F -#define ENE_SAMPLES_SIZE 4 - -/* fan input sample buffer */ -#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */ - /* each sample of normal buffer */ - -#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */ - /* if set, says that sample is pulse */ -#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */ - -/* first firmware register */ -#define ENE_FW1 0xF8F8 -#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */ -#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */ -#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */ -#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */ - -/* second firmware register */ -#define ENE_FW2 0xF8F9 -#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */ -#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */ -#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */ - /* learning input */ -#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */ -#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */ - -/* fan as input settings - only if learning capable */ -#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */ -#define ENE_FAN_AS_IN1_EN 0xCD -#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */ -#define ENE_FAN_AS_IN2_EN 0x03 -#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */ - -/* IRQ registers block (for revision B) */ -#define ENEB_IRQ 0xFD09 /* IRQ number */ -#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */ -#define ENEB_IRQ_STATUS 0xFD80 /* irq status */ -#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */ - -/* IRQ registers block (for revision C,D) */ -#define ENEC_IRQ 0xFE9B /* new irq settings register */ -#define ENEC_IRQ_MASK 0x0F /* irq number mask */ -#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */ -#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */ - -/* CIR block settings */ -#define ENE_CIR_CONF1 0xFEC0 -#define ENE_CIR_CONF1_ADC_ON 0x7 /* receiver on gpio40 enabled */ -#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */ -#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */ -#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */ - -#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */ -#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */ -#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */ - -#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */ -#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */ - - -/* transmitter - not implemented yet */ -/* KB3926C and higher */ -/* transmission is very similar to receiving, a byte is written to */ -/* ENE_TX_INPUT, in same manner as it is read from sample buffer */ -/* sample period is fixed*/ - - -/* transmitter ports */ -#define ENE_TX_PORT1 0xFC01 /* this enables one or both */ -#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */ -#define ENE_TX_PORT2 0xFC08 -#define ENE_TX_PORT2_EN (1 << 1) - -#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */ -#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */ -#define ENE_TX_UNK1 0xFECB /* set to 0x63 */ -#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */ - - -#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */ -#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */ -#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */ - -/* Hardware versions */ -#define ENE_HW_VERSION 0xFF00 /* hardware revision */ -#define ENE_HW_UNK 0xFF1D -#define ENE_HW_UNK_CLR (1 << 2) -#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */ -#define ENE_HW_VER_MINOR 0xFF1F -#define ENE_HW_VER_OLD 0xFD00 - -#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0)) - -#define ENE_DRIVER_NAME "enecir" -#define ENE_MAXGAP 250000 /* this is amount of time we wait - before turning the sampler, chosen - arbitry */ - -#define space(len) (-(len)) /* add a space */ - -/* software defines */ -#define ENE_IRQ_RX 1 -#define ENE_IRQ_TX 2 - -#define ENE_HW_B 1 /* 3926B */ -#define ENE_HW_C 2 /* 3926C */ -#define ENE_HW_D 3 /* 3926D */ - -#define ene_printk(level, text, ...) \ - printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__) - -struct ene_device { - struct pnp_dev *pnp_dev; - struct lirc_driver *lirc_driver; - - /* hw settings */ - unsigned long hw_io; - int irq; - - int hw_revision; /* hardware revision */ - int hw_learning_and_tx_capable; /* learning capable */ - int hw_gpio40_learning; /* gpio40 is learning */ - int hw_fan_as_normal_input; /* fan input is used as regular input */ - - /* device data */ - int idle; - int fan_input_inuse; - - int sample; - int in_use; - - struct timeval gap_start; -}; diff --git a/drivers/staging/lirc/lirc_igorplugusb.c b/drivers/staging/lirc/lirc_igorplugusb.c deleted file mode 100644 index 0dc2c2b22c2b..000000000000 --- a/drivers/staging/lirc/lirc_igorplugusb.c +++ /dev/null @@ -1,577 +0,0 @@ -/* - * lirc_igorplugusb - USB remote support for LIRC - * - * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware. - * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm - * - * The device can only record bursts of up to 36 pulses/spaces. - * Works fine with RC5. Longer commands lead to device buffer overrun. - * (Maybe a better firmware or a microcontroller with more ram can help?) - * - * Version 0.1 [beta status] - * - * Copyright (C) 2004 Jan M. Hochstein - * - * - * This driver was derived from: - * Paul Miller - * "lirc_atiusb" module - * Vladimir Dergachev 's 2002 - * "USB ATI Remote support" (input device) - * Adrian Dewhurst 's 2002 - * "USB StreamZap remote driver" (LIRC) - * Artur Lipowski 's 2002 - * "lirc_dev" and "lirc_gpio" LIRC 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 - - -/* module identification */ -#define DRIVER_VERSION "0.2" -#define DRIVER_AUTHOR \ - "Jan M. Hochstein " -#define DRIVER_DESC "Igorplug USB remote driver for LIRC" -#define DRIVER_NAME "lirc_igorplugusb" - -/* debugging support */ -#ifdef CONFIG_USB_DEBUG -static int debug = 1; -#else -static int debug; -#endif - -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG fmt, ## args); \ - } while (0) - -/* One mode2 pulse/space has 4 bytes. */ -#define CODE_LENGTH sizeof(int) - -/* Igor's firmware cannot record bursts longer than 36. */ -#define DEVICE_BUFLEN 36 - -/* - * Header at the beginning of the device's buffer: - * unsigned char data_length - * unsigned char data_start (!=0 means ring-buffer overrun) - * unsigned char counter (incremented by each burst) - */ -#define DEVICE_HEADERLEN 3 - -/* This is for the gap */ -#define ADDITIONAL_LIRC_BYTES 2 - -/* times to poll per second */ -#define SAMPLE_RATE 100 -static int sample_rate = SAMPLE_RATE; - - -/**** Igor's USB Request Codes */ - -#define SET_INFRABUFFER_EMPTY 1 -/** - * Params: none - * Answer: empty - */ - -#define GET_INFRACODE 2 -/** - * Params: - * wValue: offset to begin reading infra buffer - * - * Answer: infra data - */ - -#define SET_DATAPORT_DIRECTION 3 -/** - * Params: - * wValue: (byte) 1 bit for each data port pin (0=in, 1=out) - * - * Answer: empty - */ - -#define GET_DATAPORT_DIRECTION 4 -/** - * Params: none - * - * Answer: (byte) 1 bit for each data port pin (0=in, 1=out) - */ - -#define SET_OUT_DATAPORT 5 -/** - * Params: - * wValue: byte to write to output data port - * - * Answer: empty - */ - -#define GET_OUT_DATAPORT 6 -/** - * Params: none - * - * Answer: least significant 3 bits read from output data port - */ - -#define GET_IN_DATAPORT 7 -/** - * Params: none - * - * Answer: least significant 3 bits read from input data port - */ - -#define READ_EEPROM 8 -/** - * Params: - * wValue: offset to begin reading EEPROM - * - * Answer: EEPROM bytes - */ - -#define WRITE_EEPROM 9 -/** - * Params: - * wValue: offset to EEPROM byte - * wIndex: byte to write - * - * Answer: empty - */ - -#define SEND_RS232 10 -/** - * Params: - * wValue: byte to send - * - * Answer: empty - */ - -#define RECV_RS232 11 -/** - * Params: none - * - * Answer: byte received - */ - -#define SET_RS232_BAUD 12 -/** - * Params: - * wValue: byte to write to UART bit rate register (UBRR) - * - * Answer: empty - */ - -#define GET_RS232_BAUD 13 -/** - * Params: none - * - * Answer: byte read from UART bit rate register (UBRR) - */ - - -/* data structure for each usb remote */ -struct igorplug { - - /* usb */ - struct usb_device *usbdev; - int devnum; - - unsigned char *buf_in; - unsigned int len_in; - int in_space; - struct timeval last_time; - - dma_addr_t dma_in; - - /* lirc */ - struct lirc_driver *d; - - /* handle sending (init strings) */ - int send_flags; -}; - -static int unregister_from_lirc(struct igorplug *ir) -{ - struct lirc_driver *d; - int devnum; - - if (!ir) { - printk(KERN_ERR "%s: called with NULL device struct!\n", - __func__); - return -EINVAL; - } - - devnum = ir->devnum; - d = ir->d; - - if (!d) { - printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", - __func__); - return -EINVAL; - } - - dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum); - lirc_unregister_driver(d->minor); - - kfree(d); - ir->d = NULL; - kfree(ir); - - return devnum; -} - -static int set_use_inc(void *data) -{ - struct igorplug *ir = data; - - if (!ir) { - printk(DRIVER_NAME "[?]: set_use_inc called with no context\n"); - return -EIO; - } - - dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum); - - if (!ir->usbdev) - return -ENODEV; - - return 0; -} - -static void set_use_dec(void *data) -{ - struct igorplug *ir = data; - - if (!ir) { - printk(DRIVER_NAME "[?]: set_use_dec called with no context\n"); - return; - } - - dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum); -} - -static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf, - int i, int max) -{ - int code; - - /* MODE2: pulse/space (PULSE_BIT) in 1us units */ - while (i < max) { - /* 1 Igor-tick = 85.333333 us */ - code = (unsigned int)ir->buf_in[i] * 85 + - (unsigned int)ir->buf_in[i] / 3; - ir->last_time.tv_usec += code; - if (ir->in_space) - code |= PULSE_BIT; - lirc_buffer_write(buf, (unsigned char *)&code); - /* 1 chunk = CODE_LENGTH bytes */ - ir->in_space ^= 1; - ++i; - } -} - -/** - * Called in user context. - * return 0 if data was added to the buffer and - * -ENODATA if none was available. This should add some number of bits - * evenly divisible by code_length to the buffer - */ -static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) -{ - int ret; - struct igorplug *ir = (struct igorplug *)data; - - if (!ir || !ir->usbdev) /* Has the device been removed? */ - return -ENODEV; - - memset(ir->buf_in, 0, ir->len_in); - - ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), - GET_INFRACODE, USB_TYPE_VENDOR | USB_DIR_IN, - 0/* offset */, /*unused*/0, - ir->buf_in, ir->len_in, - /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); - if (ret > 0) { - int code, timediff; - struct timeval now; - - /* ACK packet has 1 byte --> ignore */ - if (ret < DEVICE_HEADERLEN) - return -ENODATA; - - dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n", - ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]); - - do_gettimeofday(&now); - timediff = now.tv_sec - ir->last_time.tv_sec; - if (timediff + 1 > PULSE_MASK / 1000000) - timediff = PULSE_MASK; - else { - timediff *= 1000000; - timediff += now.tv_usec - ir->last_time.tv_usec; - } - ir->last_time.tv_sec = now.tv_sec; - ir->last_time.tv_usec = now.tv_usec; - - /* create leading gap */ - code = timediff; - lirc_buffer_write(buf, (unsigned char *)&code); - ir->in_space = 1; /* next comes a pulse */ - - if (ir->buf_in[2] == 0) - send_fragment(ir, buf, DEVICE_HEADERLEN, ret); - else { - printk(KERN_WARNING DRIVER_NAME - "[%d]: Device buffer overrun.\n", ir->devnum); - /* HHHNNNNNNNNNNNOOOOOOOO H = header - <---[2]---> N = newer - <---------ret--------> O = older */ - ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */ - /* keep even-ness to not desync pulse/pause */ - send_fragment(ir, buf, DEVICE_HEADERLEN + - ir->buf_in[2] - (ir->buf_in[2] & 1), ret); - send_fragment(ir, buf, DEVICE_HEADERLEN, - DEVICE_HEADERLEN + ir->buf_in[2]); - } - - ret = usb_control_msg( - ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), - SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, - /*unused*/0, /*unused*/0, - /*dummy*/ir->buf_in, /*dummy*/ir->len_in, - /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); - if (ret < 0) - printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: " - "error %d\n", ir->devnum, ret); - return 0; - } else if (ret < 0) - printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n", - ir->devnum, ret); - - return -ENODATA; -} - - - -static int igorplugusb_remote_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = NULL; - struct usb_host_interface *idesc = NULL; - struct usb_endpoint_descriptor *ep; - struct igorplug *ir = NULL; - struct lirc_driver *driver = NULL; - int devnum, pipe, maxp; - int minor = 0; - char buf[63], name[128] = ""; - int mem_failure = 0; - int ret; - - dprintk(DRIVER_NAME ": usb probe called.\n"); - - dev = interface_to_usbdev(intf); - - idesc = intf->cur_altsetting; - - if (idesc->desc.bNumEndpoints != 1) - return -ENODEV; - - ep = &idesc->endpoint->desc; - if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) - != USB_DIR_IN) - || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) - != USB_ENDPOINT_XFER_CONTROL) - return -ENODEV; - - pipe = usb_rcvctrlpipe(dev, ep->bEndpointAddress); - devnum = dev->devnum; - maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - - dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n", - devnum, CODE_LENGTH, maxp); - - mem_failure = 0; - ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL); - if (!ir) { - mem_failure = 1; - goto mem_failure_switch; - } - driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); - if (!driver) { - mem_failure = 2; - goto mem_failure_switch; - } - - ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, - GFP_ATOMIC, &ir->dma_in); - if (!ir->buf_in) { - mem_failure = 3; - goto mem_failure_switch; - } - - strcpy(driver->name, DRIVER_NAME " "); - driver->minor = -1; - driver->code_length = CODE_LENGTH * 8; /* in bits */ - driver->features = LIRC_CAN_REC_MODE2; - driver->data = ir; - driver->chunk_size = CODE_LENGTH; - driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES; - driver->set_use_inc = &set_use_inc; - driver->set_use_dec = &set_use_dec; - driver->sample_rate = sample_rate; /* per second */ - driver->add_to_buf = &igorplugusb_remote_poll; - driver->dev = &intf->dev; - driver->owner = THIS_MODULE; - - minor = lirc_register_driver(driver); - if (minor < 0) - mem_failure = 9; - -mem_failure_switch: - - switch (mem_failure) { - case 9: - usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, - ir->buf_in, ir->dma_in); - case 3: - kfree(driver); - case 2: - kfree(ir); - case 1: - printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", - devnum, mem_failure); - return -ENOMEM; - } - - driver->minor = minor; - ir->d = driver; - ir->devnum = devnum; - ir->usbdev = dev; - ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN; - ir->in_space = 1; /* First mode2 event is a space. */ - do_gettimeofday(&ir->last_time); - - if (dev->descriptor.iManufacturer - && usb_string(dev, dev->descriptor.iManufacturer, - buf, sizeof(buf)) > 0) - strlcpy(name, buf, sizeof(name)); - if (dev->descriptor.iProduct - && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0) - snprintf(name + strlen(name), sizeof(name) - strlen(name), - " %s", buf); - printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name, - dev->bus->busnum, devnum); - - /* clear device buffer */ - ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), - SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, - /*unused*/0, /*unused*/0, - /*dummy*/ir->buf_in, /*dummy*/ir->len_in, - /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); - if (ret < 0) - printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", - devnum, ret); - - usb_set_intfdata(intf, ir); - return 0; -} - - -static void igorplugusb_remote_disconnect(struct usb_interface *intf) -{ - struct usb_device *usbdev = interface_to_usbdev(intf); - struct igorplug *ir = usb_get_intfdata(intf); - struct device *dev = &intf->dev; - int devnum; - - usb_set_intfdata(intf, NULL); - - if (!ir || !ir->d) - return; - - ir->usbdev = NULL; - - usb_free_coherent(usbdev, ir->len_in, ir->buf_in, ir->dma_in); - - devnum = unregister_from_lirc(ir); - - dev_info(dev, DRIVER_NAME "[%d]: %s done\n", devnum, __func__); -} - -static struct usb_device_id igorplugusb_remote_id_table[] = { - /* Igor Plug USB (Atmel's Manufact. ID) */ - { USB_DEVICE(0x03eb, 0x0002) }, - /* Fit PC2 Infrared Adapter */ - { USB_DEVICE(0x03eb, 0x21fe) }, - - /* Terminating entry */ - { } -}; - -static struct usb_driver igorplugusb_remote_driver = { - .name = DRIVER_NAME, - .probe = igorplugusb_remote_probe, - .disconnect = igorplugusb_remote_disconnect, - .id_table = igorplugusb_remote_id_table -}; - -static int __init igorplugusb_remote_init(void) -{ - int ret = 0; - - dprintk(DRIVER_NAME ": loaded, debug mode enabled\n"); - - ret = usb_register(&igorplugusb_remote_driver); - if (ret) - printk(KERN_ERR DRIVER_NAME ": usb register failed!\n"); - - return ret; -} - -static void __exit igorplugusb_remote_exit(void) -{ - usb_deregister(&igorplugusb_remote_driver); -} - -module_init(igorplugusb_remote_init); -module_exit(igorplugusb_remote_exit); - -#include -MODULE_INFO(vermagic, VERMAGIC_STRING); - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(usb, igorplugusb_remote_id_table); - -module_param(sample_rate, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/staging/lirc/lirc_imon.c b/drivers/staging/lirc/lirc_imon.c deleted file mode 100644 index f5308d5929c6..000000000000 --- a/drivers/staging/lirc/lirc_imon.c +++ /dev/null @@ -1,1050 +0,0 @@ -/* - * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD - * including the iMON PAD model - * - * Copyright(C) 2004 Venky Raju(dev@venky.ws) - * Copyright(C) 2009 Jarod Wilson - * - * lirc_imon is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU 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 - - -#define MOD_AUTHOR "Venky Raju " -#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display" -#define MOD_NAME "lirc_imon" -#define MOD_VERSION "0.8" - -#define DISPLAY_MINOR_BASE 144 -#define DEVICE_NAME "lcd%d" - -#define BUF_CHUNK_SIZE 4 -#define BUF_SIZE 128 - -#define BIT_DURATION 250 /* each bit received is 250us */ - -/*** P R O T O T Y P E S ***/ - -/* USB Callback prototypes */ -static int imon_probe(struct usb_interface *interface, - const struct usb_device_id *id); -static void imon_disconnect(struct usb_interface *interface); -static void usb_rx_callback(struct urb *urb); -static void usb_tx_callback(struct urb *urb); - -/* suspend/resume support */ -static int imon_resume(struct usb_interface *intf); -static int imon_suspend(struct usb_interface *intf, pm_message_t message); - -/* Display file_operations function prototypes */ -static int display_open(struct inode *inode, struct file *file); -static int display_close(struct inode *inode, struct file *file); - -/* VFD write operation */ -static ssize_t vfd_write(struct file *file, const char *buf, - size_t n_bytes, loff_t *pos); - -/* LIRC driver function prototypes */ -static int ir_open(void *data); -static void ir_close(void *data); - -/* Driver init/exit prototypes */ -static int __init imon_init(void); -static void __exit imon_exit(void); - -/*** G L O B A L S ***/ -#define IMON_DATA_BUF_SZ 35 - -struct imon_context { - struct usb_device *usbdev; - /* Newer devices have two interfaces */ - int display; /* not all controllers do */ - int display_isopen; /* display port has been opened */ - int ir_isopen; /* IR port open */ - int dev_present; /* USB device presence */ - struct mutex ctx_lock; /* to lock this object */ - wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ - - int vfd_proto_6p; /* some VFD require a 6th packet */ - - struct lirc_driver *driver; - struct usb_endpoint_descriptor *rx_endpoint; - struct usb_endpoint_descriptor *tx_endpoint; - struct urb *rx_urb; - struct urb *tx_urb; - unsigned char usb_rx_buf[8]; - unsigned char usb_tx_buf[8]; - - struct rx_data { - int count; /* length of 0 or 1 sequence */ - int prev_bit; /* logic level of sequence */ - int initial_space; /* initial space flag */ - } rx; - - struct tx_t { - unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */ - struct completion finished; /* wait for write to finish */ - atomic_t busy; /* write in progress */ - int status; /* status of tx completion */ - } tx; -}; - -static const struct file_operations display_fops = { - .owner = THIS_MODULE, - .open = &display_open, - .write = &vfd_write, - .release = &display_close, - .llseek = noop_llseek, -}; - -/* - * USB Device ID for iMON USB Control Boards - * - * The Windows drivers contain 6 different inf files, more or less one for - * each new device until the 0x0034-0x0046 devices, which all use the same - * driver. Some of the devices in the 34-46 range haven't been definitively - * identified yet. Early devices have either a TriGem Computer, Inc. or a - * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later - * devices use the SoundGraph vendor ID (0x15c2). - */ -static struct usb_device_id imon_usb_id_table[] = { - /* TriGem iMON (IR only) -- TG_iMON.inf */ - { USB_DEVICE(0x0aa8, 0x8001) }, - - /* SoundGraph iMON (IR only) -- sg_imon.inf */ - { USB_DEVICE(0x04e8, 0xff30) }, - - /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */ - { USB_DEVICE(0x0aa8, 0xffda) }, - - /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */ - { USB_DEVICE(0x15c2, 0xffda) }, - - {} -}; - -/* Some iMON VFD models requires a 6th packet for VFD writes */ -static struct usb_device_id vfd_proto_6p_list[] = { - { USB_DEVICE(0x15c2, 0xffda) }, - {} -}; - -/* Some iMON devices have no lcd/vfd, don't set one up */ -static struct usb_device_id ir_only_list[] = { - { USB_DEVICE(0x0aa8, 0x8001) }, - { USB_DEVICE(0x04e8, 0xff30) }, - {} -}; - -/* USB Device data */ -static struct usb_driver imon_driver = { - .name = MOD_NAME, - .probe = imon_probe, - .disconnect = imon_disconnect, - .suspend = imon_suspend, - .resume = imon_resume, - .id_table = imon_usb_id_table, -}; - -static struct usb_class_driver imon_class = { - .name = DEVICE_NAME, - .fops = &display_fops, - .minor_base = DISPLAY_MINOR_BASE, -}; - -/* to prevent races between open() and disconnect(), probing, etc */ -static DEFINE_MUTEX(driver_lock); - -static int debug; - -/*** M O D U L E C O D E ***/ - -MODULE_AUTHOR(MOD_AUTHOR); -MODULE_DESCRIPTION(MOD_DESC); -MODULE_VERSION(MOD_VERSION); -MODULE_LICENSE("GPL"); -MODULE_DEVICE_TABLE(usb, imon_usb_id_table); -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)"); - -static void free_imon_context(struct imon_context *context) -{ - struct device *dev = context->driver->dev; - usb_free_urb(context->tx_urb); - usb_free_urb(context->rx_urb); - lirc_buffer_free(context->driver->rbuf); - kfree(context->driver->rbuf); - kfree(context->driver); - kfree(context); - - dev_dbg(dev, "%s: iMON context freed\n", __func__); -} - -static void deregister_from_lirc(struct imon_context *context) -{ - int retval; - int minor = context->driver->minor; - - retval = lirc_unregister_driver(minor); - if (retval) - err("%s: unable to deregister from lirc(%d)", - __func__, retval); - else - printk(KERN_INFO MOD_NAME ": Deregistered iMON driver " - "(minor:%d)\n", minor); - -} - -/** - * Called when the Display device (e.g. /dev/lcd0) - * is opened by the application. - */ -static int display_open(struct inode *inode, struct file *file) -{ - struct usb_interface *interface; - struct imon_context *context = NULL; - int subminor; - int retval = 0; - - /* prevent races with disconnect */ - mutex_lock(&driver_lock); - - subminor = iminor(inode); - interface = usb_find_interface(&imon_driver, subminor); - if (!interface) { - err("%s: could not find interface for minor %d", - __func__, subminor); - retval = -ENODEV; - goto exit; - } - context = usb_get_intfdata(interface); - - if (!context) { - err("%s: no context found for minor %d", - __func__, subminor); - retval = -ENODEV; - goto exit; - } - - mutex_lock(&context->ctx_lock); - - if (!context->display) { - err("%s: display not supported by device", __func__); - retval = -ENODEV; - } else if (context->display_isopen) { - err("%s: display port is already open", __func__); - retval = -EBUSY; - } else { - context->display_isopen = 1; - file->private_data = context; - dev_info(context->driver->dev, "display port opened\n"); - } - - mutex_unlock(&context->ctx_lock); - -exit: - mutex_unlock(&driver_lock); - return retval; -} - -/** - * Called when the display device (e.g. /dev/lcd0) - * is closed by the application. - */ -static int display_close(struct inode *inode, struct file *file) -{ - struct imon_context *context = NULL; - int retval = 0; - - context = file->private_data; - - if (!context) { - err("%s: no context for device", __func__); - return -ENODEV; - } - - mutex_lock(&context->ctx_lock); - - if (!context->display) { - err("%s: display not supported by device", __func__); - retval = -ENODEV; - } else if (!context->display_isopen) { - err("%s: display is not open", __func__); - retval = -EIO; - } else { - context->display_isopen = 0; - dev_info(context->driver->dev, "display port closed\n"); - if (!context->dev_present && !context->ir_isopen) { - /* - * Device disconnected before close and IR port is not - * open. If IR port is open, context will be deleted by - * ir_close. - */ - mutex_unlock(&context->ctx_lock); - free_imon_context(context); - return retval; - } - } - - mutex_unlock(&context->ctx_lock); - return retval; -} - -/** - * Sends a packet to the device -- this function must be called - * with context->ctx_lock held. - */ -static int send_packet(struct imon_context *context) -{ - unsigned int pipe; - int interval = 0; - int retval = 0; - - /* Check if we need to use control or interrupt urb */ - pipe = usb_sndintpipe(context->usbdev, - context->tx_endpoint->bEndpointAddress); - interval = context->tx_endpoint->bInterval; - - usb_fill_int_urb(context->tx_urb, context->usbdev, pipe, - context->usb_tx_buf, - sizeof(context->usb_tx_buf), - usb_tx_callback, context, interval); - - context->tx_urb->actual_length = 0; - - init_completion(&context->tx.finished); - atomic_set(&(context->tx.busy), 1); - - retval = usb_submit_urb(context->tx_urb, GFP_KERNEL); - if (retval) { - atomic_set(&(context->tx.busy), 0); - err("%s: error submitting urb(%d)", __func__, retval); - } else { - /* Wait for transmission to complete (or abort) */ - mutex_unlock(&context->ctx_lock); - retval = wait_for_completion_interruptible( - &context->tx.finished); - if (retval) - err("%s: task interrupted", __func__); - mutex_lock(&context->ctx_lock); - - retval = context->tx.status; - if (retval) - err("%s: packet tx failed (%d)", __func__, retval); - } - - return retval; -} - -/** - * Writes data to the VFD. The iMON VFD is 2x16 characters - * and requires data in 5 consecutive USB interrupt packets, - * each packet but the last carrying 7 bytes. - * - * I don't know if the VFD board supports features such as - * scrolling, clearing rows, blanking, etc. so at - * the caller must provide a full screen of data. If fewer - * than 32 bytes are provided spaces will be appended to - * generate a full screen. - */ -static ssize_t vfd_write(struct file *file, const char *buf, - size_t n_bytes, loff_t *pos) -{ - int i; - int offset; - int seq; - int retval = 0; - struct imon_context *context; - const unsigned char vfd_packet6[] = { - 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; - int *data_buf = NULL; - - context = file->private_data; - if (!context) { - err("%s: no context for device", __func__); - return -ENODEV; - } - - mutex_lock(&context->ctx_lock); - - if (!context->dev_present) { - err("%s: no iMON device present", __func__); - retval = -ENODEV; - goto exit; - } - - if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) { - err("%s: invalid payload size", __func__); - retval = -EINVAL; - goto exit; - } - - data_buf = memdup_user(buf, n_bytes); - if (IS_ERR(data_buf)) { - retval = PTR_ERR(data_buf); - goto exit; - } - - memcpy(context->tx.data_buf, data_buf, n_bytes); - - /* Pad with spaces */ - for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i) - context->tx.data_buf[i] = ' '; - - for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i) - context->tx.data_buf[i] = 0xFF; - - offset = 0; - seq = 0; - - do { - memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7); - context->usb_tx_buf[7] = (unsigned char) seq; - - retval = send_packet(context); - if (retval) { - err("%s: send packet failed for packet #%d", - __func__, seq/2); - goto exit; - } else { - seq += 2; - offset += 7; - } - - } while (offset < IMON_DATA_BUF_SZ); - - if (context->vfd_proto_6p) { - /* Send packet #6 */ - memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6)); - context->usb_tx_buf[7] = (unsigned char) seq; - retval = send_packet(context); - if (retval) - err("%s: send packet failed for packet #%d", - __func__, seq/2); - } - -exit: - mutex_unlock(&context->ctx_lock); - kfree(data_buf); - - return (!retval) ? n_bytes : retval; -} - -/** - * Callback function for USB core API: transmit data - */ -static void usb_tx_callback(struct urb *urb) -{ - struct imon_context *context; - - if (!urb) - return; - context = (struct imon_context *)urb->context; - if (!context) - return; - - context->tx.status = urb->status; - - /* notify waiters that write has finished */ - atomic_set(&context->tx.busy, 0); - complete(&context->tx.finished); - - return; -} - -/** - * Called by lirc_dev when the application opens /dev/lirc - */ -static int ir_open(void *data) -{ - int retval = 0; - struct imon_context *context; - - /* prevent races with disconnect */ - mutex_lock(&driver_lock); - - context = (struct imon_context *)data; - - /* initial IR protocol decode variables */ - context->rx.count = 0; - context->rx.initial_space = 1; - context->rx.prev_bit = 0; - - context->ir_isopen = 1; - dev_info(context->driver->dev, "IR port opened\n"); - - mutex_unlock(&driver_lock); - return retval; -} - -/** - * Called by lirc_dev when the application closes /dev/lirc - */ -static void ir_close(void *data) -{ - struct imon_context *context; - - context = (struct imon_context *)data; - if (!context) { - err("%s: no context for device", __func__); - return; - } - - mutex_lock(&context->ctx_lock); - - context->ir_isopen = 0; - dev_info(context->driver->dev, "IR port closed\n"); - - if (!context->dev_present) { - /* - * Device disconnected while IR port was still open. Driver - * was not deregistered at disconnect time, so do it now. - */ - deregister_from_lirc(context); - - if (!context->display_isopen) { - mutex_unlock(&context->ctx_lock); - free_imon_context(context); - return; - } - /* - * If display port is open, context will be deleted by - * display_close - */ - } - - mutex_unlock(&context->ctx_lock); - return; -} - -/** - * Convert bit count to time duration (in us) and submit - * the value to lirc_dev. - */ -static void submit_data(struct imon_context *context) -{ - unsigned char buf[4]; - int value = context->rx.count; - int i; - - dev_dbg(context->driver->dev, "submitting data to LIRC\n"); - - value *= BIT_DURATION; - value &= PULSE_MASK; - if (context->rx.prev_bit) - value |= PULSE_BIT; - - for (i = 0; i < 4; ++i) - buf[i] = value>>(i*8); - - lirc_buffer_write(context->driver->rbuf, buf); - wake_up(&context->driver->rbuf->wait_poll); - return; -} - -static inline int tv2int(const struct timeval *a, const struct timeval *b) -{ - int usecs = 0; - int sec = 0; - - if (b->tv_usec > a->tv_usec) { - usecs = 1000000; - sec--; - } - - usecs += a->tv_usec - b->tv_usec; - - sec += a->tv_sec - b->tv_sec; - sec *= 1000; - usecs /= 1000; - sec += usecs; - - if (sec < 0) - sec = 1000; - - return sec; -} - -/** - * Process the incoming packet - */ -static void imon_incoming_packet(struct imon_context *context, - struct urb *urb, int intf) -{ - int len = urb->actual_length; - unsigned char *buf = urb->transfer_buffer; - struct device *dev = context->driver->dev; - int octet, bit; - unsigned char mask; - int i; - - /* - * just bail out if no listening IR client - */ - if (!context->ir_isopen) - return; - - if (len != 8) { - dev_warn(dev, "imon %s: invalid incoming packet " - "size (len = %d, intf%d)\n", __func__, len, intf); - return; - } - - if (debug) { - printk(KERN_INFO "raw packet: "); - for (i = 0; i < len; ++i) - printk("%02x ", buf[i]); - printk("\n"); - } - - /* - * Translate received data to pulse and space lengths. - * Received data is active low, i.e. pulses are 0 and - * spaces are 1. - * - * My original algorithm was essentially similar to - * Changwoo Ryu's with the exception that he switched - * the incoming bits to active high and also fed an - * initial space to LIRC at the start of a new sequence - * if the previous bit was a pulse. - * - * I've decided to adopt his algorithm. - */ - - if (buf[7] == 1 && context->rx.initial_space) { - /* LIRC requires a leading space */ - context->rx.prev_bit = 0; - context->rx.count = 4; - submit_data(context); - context->rx.count = 0; - } - - for (octet = 0; octet < 5; ++octet) { - mask = 0x80; - for (bit = 0; bit < 8; ++bit) { - int curr_bit = !(buf[octet] & mask); - if (curr_bit != context->rx.prev_bit) { - if (context->rx.count) { - submit_data(context); - context->rx.count = 0; - } - context->rx.prev_bit = curr_bit; - } - ++context->rx.count; - mask >>= 1; - } - } - - if (buf[7] == 10) { - if (context->rx.count) { - submit_data(context); - context->rx.count = 0; - } - context->rx.initial_space = context->rx.prev_bit; - } -} - -/** - * Callback function for USB core API: receive data - */ -static void usb_rx_callback(struct urb *urb) -{ - struct imon_context *context; - int intfnum = 0; - - if (!urb) - return; - - context = (struct imon_context *)urb->context; - if (!context) - return; - - switch (urb->status) { - case -ENOENT: /* usbcore unlink successful! */ - return; - - case 0: - imon_incoming_packet(context, urb, intfnum); - break; - - default: - dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n", - __func__, urb->status); - break; - } - - usb_submit_urb(context->rx_urb, GFP_ATOMIC); - - return; -} - -/** - * Callback function for USB core API: Probe - */ -static int imon_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *usbdev = NULL; - struct usb_host_interface *iface_desc = NULL; - struct usb_endpoint_descriptor *rx_endpoint = NULL; - struct usb_endpoint_descriptor *tx_endpoint = NULL; - struct urb *rx_urb = NULL; - struct urb *tx_urb = NULL; - struct lirc_driver *driver = NULL; - struct lirc_buffer *rbuf = NULL; - struct device *dev = &interface->dev; - int ifnum; - int lirc_minor = 0; - int num_endpts; - int retval = 0; - int display_ep_found = 0; - int ir_ep_found = 0; - int alloc_status = 0; - int vfd_proto_6p = 0; - struct imon_context *context = NULL; - int i; - u16 vendor, product; - - /* prevent races probing devices w/multiple interfaces */ - mutex_lock(&driver_lock); - - context = kzalloc(sizeof(struct imon_context), GFP_KERNEL); - if (!context) { - err("%s: kzalloc failed for context", __func__); - alloc_status = 1; - goto alloc_status_switch; - } - - /* - * Try to auto-detect the type of display if the user hasn't set - * it by hand via the display_type modparam. Default is VFD. - */ - if (usb_match_id(interface, ir_only_list)) - context->display = 0; - else - context->display = 1; - - usbdev = usb_get_dev(interface_to_usbdev(interface)); - iface_desc = interface->cur_altsetting; - num_endpts = iface_desc->desc.bNumEndpoints; - ifnum = iface_desc->desc.bInterfaceNumber; - vendor = le16_to_cpu(usbdev->descriptor.idVendor); - product = le16_to_cpu(usbdev->descriptor.idProduct); - - dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n", - __func__, vendor, product, ifnum); - - /* - * Scan the endpoint list and set: - * first input endpoint = IR endpoint - * first output endpoint = display endpoint - */ - for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) { - struct usb_endpoint_descriptor *ep; - int ep_dir; - int ep_type; - ep = &iface_desc->endpoint[i].desc; - ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; - ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - - if (!ir_ep_found && - ep_dir == USB_DIR_IN && - ep_type == USB_ENDPOINT_XFER_INT) { - - rx_endpoint = ep; - ir_ep_found = 1; - dev_dbg(dev, "%s: found IR endpoint\n", __func__); - - } else if (!display_ep_found && ep_dir == USB_DIR_OUT && - ep_type == USB_ENDPOINT_XFER_INT) { - tx_endpoint = ep; - display_ep_found = 1; - dev_dbg(dev, "%s: found display endpoint\n", __func__); - } - } - - /* - * Some iMON receivers have no display. Unfortunately, it seems - * that SoundGraph recycles device IDs between devices both with - * and without... :\ - */ - if (context->display == 0) { - display_ep_found = 0; - dev_dbg(dev, "%s: device has no display\n", __func__); - } - - /* Input endpoint is mandatory */ - if (!ir_ep_found) { - err("%s: no valid input (IR) endpoint found.", __func__); - retval = -ENODEV; - alloc_status = 2; - goto alloc_status_switch; - } - - /* Determine if display requires 6 packets */ - if (display_ep_found) { - if (usb_match_id(interface, vfd_proto_6p_list)) - vfd_proto_6p = 1; - - dev_dbg(dev, "%s: vfd_proto_6p: %d\n", - __func__, vfd_proto_6p); - } - - driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); - if (!driver) { - err("%s: kzalloc failed for lirc_driver", __func__); - alloc_status = 2; - goto alloc_status_switch; - } - rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!rbuf) { - err("%s: kmalloc failed for lirc_buffer", __func__); - alloc_status = 3; - goto alloc_status_switch; - } - if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { - err("%s: lirc_buffer_init failed", __func__); - alloc_status = 4; - goto alloc_status_switch; - } - rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!rx_urb) { - err("%s: usb_alloc_urb failed for IR urb", __func__); - alloc_status = 5; - goto alloc_status_switch; - } - tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!tx_urb) { - err("%s: usb_alloc_urb failed for display urb", - __func__); - alloc_status = 6; - goto alloc_status_switch; - } - - mutex_init(&context->ctx_lock); - context->vfd_proto_6p = vfd_proto_6p; - - strcpy(driver->name, MOD_NAME); - driver->minor = -1; - driver->code_length = BUF_CHUNK_SIZE * 8; - driver->sample_rate = 0; - driver->features = LIRC_CAN_REC_MODE2; - driver->data = context; - driver->rbuf = rbuf; - driver->set_use_inc = ir_open; - driver->set_use_dec = ir_close; - driver->dev = &interface->dev; - driver->owner = THIS_MODULE; - - mutex_lock(&context->ctx_lock); - - context->driver = driver; - /* start out in keyboard mode */ - - lirc_minor = lirc_register_driver(driver); - if (lirc_minor < 0) { - err("%s: lirc_register_driver failed", __func__); - alloc_status = 7; - goto unlock; - } else - dev_info(dev, "Registered iMON driver " - "(lirc minor: %d)\n", lirc_minor); - - /* Needed while unregistering! */ - driver->minor = lirc_minor; - - context->usbdev = usbdev; - context->dev_present = 1; - context->rx_endpoint = rx_endpoint; - context->rx_urb = rx_urb; - - /* - * tx is used to send characters to lcd/vfd, associate RF - * remotes, set IR protocol, and maybe more... - */ - context->tx_endpoint = tx_endpoint; - context->tx_urb = tx_urb; - - if (display_ep_found) - context->display = 1; - - usb_fill_int_urb(context->rx_urb, context->usbdev, - usb_rcvintpipe(context->usbdev, - context->rx_endpoint->bEndpointAddress), - context->usb_rx_buf, sizeof(context->usb_rx_buf), - usb_rx_callback, context, - context->rx_endpoint->bInterval); - - retval = usb_submit_urb(context->rx_urb, GFP_KERNEL); - - if (retval) { - err("%s: usb_submit_urb failed for intf0 (%d)", - __func__, retval); - mutex_unlock(&context->ctx_lock); - goto exit; - } - - usb_set_intfdata(interface, context); - - if (context->display && ifnum == 0) { - dev_dbg(dev, "%s: Registering iMON display with sysfs\n", - __func__); - - if (usb_register_dev(interface, &imon_class)) { - /* Not a fatal error, so ignore */ - dev_info(dev, "%s: could not get a minor number for " - "display\n", __func__); - } - } - - dev_info(dev, "iMON device (%04x:%04x, intf%d) on " - "usb<%d:%d> initialized\n", vendor, product, ifnum, - usbdev->bus->busnum, usbdev->devnum); - -unlock: - mutex_unlock(&context->ctx_lock); -alloc_status_switch: - - switch (alloc_status) { - case 7: - usb_free_urb(tx_urb); - case 6: - usb_free_urb(rx_urb); - case 5: - if (rbuf) - lirc_buffer_free(rbuf); - case 4: - kfree(rbuf); - case 3: - kfree(driver); - case 2: - kfree(context); - context = NULL; - case 1: - if (retval != -ENODEV) - retval = -ENOMEM; - break; - case 0: - retval = 0; - } - -exit: - mutex_unlock(&driver_lock); - - return retval; -} - -/** - * Callback function for USB core API: disconnect - */ -static void imon_disconnect(struct usb_interface *interface) -{ - struct imon_context *context; - int ifnum; - - /* prevent races with ir_open()/display_open() */ - mutex_lock(&driver_lock); - - context = usb_get_intfdata(interface); - ifnum = interface->cur_altsetting->desc.bInterfaceNumber; - - mutex_lock(&context->ctx_lock); - - usb_set_intfdata(interface, NULL); - - /* Abort ongoing write */ - if (atomic_read(&context->tx.busy)) { - usb_kill_urb(context->tx_urb); - complete_all(&context->tx.finished); - } - - context->dev_present = 0; - usb_kill_urb(context->rx_urb); - if (context->display) - usb_deregister_dev(interface, &imon_class); - - if (!context->ir_isopen && !context->dev_present) { - deregister_from_lirc(context); - mutex_unlock(&context->ctx_lock); - if (!context->display_isopen) - free_imon_context(context); - } else - mutex_unlock(&context->ctx_lock); - - mutex_unlock(&driver_lock); - - printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n", - __func__, ifnum); -} - -static int imon_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct imon_context *context = usb_get_intfdata(intf); - - usb_kill_urb(context->rx_urb); - - return 0; -} - -static int imon_resume(struct usb_interface *intf) -{ - int rc = 0; - struct imon_context *context = usb_get_intfdata(intf); - - usb_fill_int_urb(context->rx_urb, context->usbdev, - usb_rcvintpipe(context->usbdev, - context->rx_endpoint->bEndpointAddress), - context->usb_rx_buf, sizeof(context->usb_rx_buf), - usb_rx_callback, context, - context->rx_endpoint->bInterval); - - rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC); - - return rc; -} - -static int __init imon_init(void) -{ - int rc; - - printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n"); - - rc = usb_register(&imon_driver); - if (rc) { - err("%s: usb register failed(%d)", __func__, rc); - return -ENODEV; - } - - return 0; -} - -static void __exit imon_exit(void) -{ - usb_deregister(&imon_driver); - printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n"); -} - -module_init(imon_init); -module_exit(imon_exit); diff --git a/drivers/staging/lirc/lirc_parallel.c b/drivers/staging/lirc/lirc_parallel.c deleted file mode 100644 index 792aac0a8e7b..000000000000 --- a/drivers/staging/lirc/lirc_parallel.c +++ /dev/null @@ -1,755 +0,0 @@ -/* - * lirc_parallel.c - * - * lirc_parallel - device driver for infra-red signal receiving and - * transmitting unit built by the author - * - * Copyright (C) 1998 Christoph Bartelmus - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/*** Includes ***/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -#include "lirc_parallel.h" - -#define LIRC_DRIVER_NAME "lirc_parallel" - -#ifndef LIRC_IRQ -#define LIRC_IRQ 7 -#endif -#ifndef LIRC_PORT -#define LIRC_PORT 0x378 -#endif -#ifndef LIRC_TIMER -#define LIRC_TIMER 65536 -#endif - -/*** Global Variables ***/ - -static int debug; -static int check_pselecd; - -unsigned int irq = LIRC_IRQ; -unsigned int io = LIRC_PORT; -#ifdef LIRC_TIMER -unsigned int timer; -unsigned int default_timer = LIRC_TIMER; -#endif - -#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */ - -static int rbuf[RBUF_SIZE]; - -DECLARE_WAIT_QUEUE_HEAD(lirc_wait); - -unsigned int rptr; -unsigned int wptr; -unsigned int lost_irqs; -int is_open; - -struct parport *pport; -struct pardevice *ppdevice; -int is_claimed; - -unsigned int tx_mask = 1; - -/*** Internal Functions ***/ - -static unsigned int in(int offset) -{ - switch (offset) { - case LIRC_LP_BASE: - return parport_read_data(pport); - case LIRC_LP_STATUS: - return parport_read_status(pport); - case LIRC_LP_CONTROL: - return parport_read_control(pport); - } - return 0; /* make compiler happy */ -} - -static void out(int offset, int value) -{ - switch (offset) { - case LIRC_LP_BASE: - parport_write_data(pport, value); - break; - case LIRC_LP_CONTROL: - parport_write_control(pport, value); - break; - case LIRC_LP_STATUS: - printk(KERN_INFO "%s: attempt to write to status register\n", - LIRC_DRIVER_NAME); - break; - } -} - -static unsigned int lirc_get_timer(void) -{ - return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT; -} - -static unsigned int lirc_get_signal(void) -{ - return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT; -} - -static void lirc_on(void) -{ - out(LIRC_PORT_DATA, tx_mask); -} - -static void lirc_off(void) -{ - out(LIRC_PORT_DATA, 0); -} - -static unsigned int init_lirc_timer(void) -{ - struct timeval tv, now; - unsigned int level, newlevel, timeelapsed, newtimer; - int count = 0; - - do_gettimeofday(&tv); - tv.tv_sec++; /* wait max. 1 sec. */ - level = lirc_get_timer(); - do { - newlevel = lirc_get_timer(); - if (level == 0 && newlevel != 0) - count++; - level = newlevel; - do_gettimeofday(&now); - } while (count < 1000 && (now.tv_sec < tv.tv_sec - || (now.tv_sec == tv.tv_sec - && now.tv_usec < tv.tv_usec))); - - timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000 - + (now.tv_usec - tv.tv_usec)); - if (count >= 1000 && timeelapsed > 0) { - if (default_timer == 0) { - /* autodetect timer */ - newtimer = (1000000*count)/timeelapsed; - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); - return newtimer; - } else { - newtimer = (1000000*count)/timeelapsed; - if (abs(newtimer - default_timer) > default_timer/10) { - /* bad timer */ - printk(KERN_NOTICE "%s: bad timer: %u Hz\n", - LIRC_DRIVER_NAME, newtimer); - printk(KERN_NOTICE "%s: using default timer: " - "%u Hz\n", - LIRC_DRIVER_NAME, default_timer); - return default_timer; - } else { - printk(KERN_INFO "%s: %u Hz timer detected\n", - LIRC_DRIVER_NAME, newtimer); - return newtimer; /* use detected value */ - } - } - } else { - printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); - return 0; - } -} - -static int lirc_claim(void) -{ - if (parport_claim(ppdevice) != 0) { - printk(KERN_WARNING "%s: could not claim port\n", - LIRC_DRIVER_NAME); - printk(KERN_WARNING "%s: waiting for port becoming available" - "\n", LIRC_DRIVER_NAME); - if (parport_claim_or_block(ppdevice) < 0) { - printk(KERN_NOTICE "%s: could not claim port, giving" - " up\n", LIRC_DRIVER_NAME); - return 0; - } - } - out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); - is_claimed = 1; - return 1; -} - -/*** interrupt handler ***/ - -static void rbuf_write(int signal) -{ - unsigned int nwptr; - - nwptr = (wptr + 1) & (RBUF_SIZE - 1); - if (nwptr == rptr) { - /* no new signals will be accepted */ - lost_irqs++; - printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); - return; - } - rbuf[wptr] = signal; - wptr = nwptr; -} - -static void irq_handler(void *blah) -{ - struct timeval tv; - static struct timeval lasttv; - static int init; - long signal; - int data; - unsigned int level, newlevel; - unsigned int timeout; - - if (!is_open) - return; - - if (!is_claimed) - return; - -#if 0 - /* disable interrupt */ - disable_irq(irq); - out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN)); -#endif - if (check_pselecd && (in(1) & LP_PSELECD)) - return; - -#ifdef LIRC_TIMER - if (init) { - do_gettimeofday(&tv); - - signal = tv.tv_sec - lasttv.tv_sec; - if (signal > 15) - /* really long time */ - data = PULSE_MASK; - else - data = (int) (signal*1000000 + - tv.tv_usec - lasttv.tv_usec + - LIRC_SFH506_DELAY); - - rbuf_write(data); /* space */ - } else { - if (timer == 0) { - /* - * wake up; we'll lose this signal, but it will be - * garbage if the device is turned on anyway - */ - timer = init_lirc_timer(); - /* enable_irq(irq); */ - return; - } - init = 1; - } - - timeout = timer/10; /* timeout after 1/10 sec. */ - signal = 1; - level = lirc_get_timer(); - do { - newlevel = lirc_get_timer(); - if (level == 0 && newlevel != 0) - signal++; - level = newlevel; - - /* giving up */ - if (signal > timeout - || (check_pselecd && (in(1) & LP_PSELECD))) { - signal = 0; - printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); - break; - } - } while (lirc_get_signal()); - - if (signal != 0) { - /* adjust value to usecs */ - __u64 helper; - - helper = ((__u64) signal)*1000000; - do_div(helper, timer); - signal = (long) helper; - - if (signal > LIRC_SFH506_DELAY) - data = signal - LIRC_SFH506_DELAY; - else - data = 1; - rbuf_write(PULSE_BIT|data); /* pulse */ - } - do_gettimeofday(&lasttv); -#else - /* add your code here */ -#endif - - wake_up_interruptible(&lirc_wait); - - /* enable interrupt */ - /* - enable_irq(irq); - out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN); - */ -} - -/*** file operations ***/ - -static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig) -{ - return -ESPIPE; -} - -static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos) -{ - int result = 0; - int count = 0; - DECLARE_WAITQUEUE(wait, current); - - if (n % sizeof(int)) - return -EINVAL; - - add_wait_queue(&lirc_wait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (count < n) { - if (rptr != wptr) { - if (copy_to_user(buf+count, (char *) &rbuf[rptr], - sizeof(int))) { - result = -EFAULT; - break; - } - rptr = (rptr + 1) & (RBUF_SIZE - 1); - count += sizeof(int); - } else { - if (filep->f_flags & O_NONBLOCK) { - result = -EAGAIN; - break; - } - if (signal_pending(current)) { - result = -ERESTARTSYS; - break; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - } - remove_wait_queue(&lirc_wait, &wait); - set_current_state(TASK_RUNNING); - return count ? count : result; -} - -static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, - loff_t *ppos) -{ - int count; - unsigned int i; - unsigned int level, newlevel; - unsigned long flags; - int counttimer; - int *wbuf; - ssize_t ret; - - if (!is_claimed) - return -EBUSY; - - count = n / sizeof(int); - - if (n % sizeof(int) || count % 2 == 0) - return -EINVAL; - - wbuf = memdup_user(buf, n); - if (IS_ERR(wbuf)) - return PTR_ERR(wbuf); - -#ifdef LIRC_TIMER - if (timer == 0) { - /* try again if device is ready */ - timer = init_lirc_timer(); - if (timer == 0) { - ret = -EIO; - goto out; - } - } - - /* adjust values from usecs */ - for (i = 0; i < count; i++) { - __u64 helper; - - helper = ((__u64) wbuf[i])*timer; - do_div(helper, 1000000); - wbuf[i] = (int) helper; - } - - local_irq_save(flags); - i = 0; - while (i < count) { - level = lirc_get_timer(); - counttimer = 0; - lirc_on(); - do { - newlevel = lirc_get_timer(); - if (level == 0 && newlevel != 0) - counttimer++; - level = newlevel; - if (check_pselecd && (in(1) & LP_PSELECD)) { - lirc_off(); - local_irq_restore(flags); - ret = -EIO; - goto out; - } - } while (counttimer < wbuf[i]); - i++; - - lirc_off(); - if (i == count) - break; - counttimer = 0; - do { - newlevel = lirc_get_timer(); - if (level == 0 && newlevel != 0) - counttimer++; - level = newlevel; - if (check_pselecd && (in(1) & LP_PSELECD)) { - local_irq_restore(flags); - ret = -EIO; - goto out; - } - } while (counttimer < wbuf[i]); - i++; - } - local_irq_restore(flags); -#else - /* place code that handles write without external timer here */ -#endif - ret = n; -out: - kfree(wbuf); - - return ret; -} - -static unsigned int lirc_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &lirc_wait, wait); - if (rptr != wptr) - return POLLIN | POLLRDNORM; - return 0; -} - -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - int result; - __u32 features = LIRC_CAN_SET_TRANSMITTER_MASK | - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; - __u32 mode; - __u32 value; - - switch (cmd) { - case LIRC_GET_FEATURES: - result = put_user(features, (__u32 *) arg); - if (result) - return result; - break; - case LIRC_GET_SEND_MODE: - result = put_user(LIRC_MODE_PULSE, (__u32 *) arg); - if (result) - return result; - break; - case LIRC_GET_REC_MODE: - result = put_user(LIRC_MODE_MODE2, (__u32 *) arg); - if (result) - return result; - break; - case LIRC_SET_SEND_MODE: - result = get_user(mode, (__u32 *) arg); - if (result) - return result; - if (mode != LIRC_MODE_PULSE) - return -EINVAL; - break; - case LIRC_SET_REC_MODE: - result = get_user(mode, (__u32 *) arg); - if (result) - return result; - if (mode != LIRC_MODE_MODE2) - return -ENOSYS; - break; - case LIRC_SET_TRANSMITTER_MASK: - result = get_user(value, (__u32 *) arg); - if (result) - return result; - if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value) - return LIRC_PARALLEL_MAX_TRANSMITTERS; - tx_mask = value; - break; - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static int lirc_open(struct inode *node, struct file *filep) -{ - if (is_open || !lirc_claim()) - return -EBUSY; - - parport_enable_irq(pport); - - /* init read ptr */ - rptr = 0; - wptr = 0; - lost_irqs = 0; - - is_open = 1; - return 0; -} - -static int lirc_close(struct inode *node, struct file *filep) -{ - if (is_claimed) { - is_claimed = 0; - parport_release(ppdevice); - } - is_open = 0; - return 0; -} - -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .llseek = lirc_lseek, - .read = lirc_read, - .write = lirc_write, - .poll = lirc_poll, - .unlocked_ioctl = lirc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lirc_ioctl, -#endif - .open = lirc_open, - .release = lirc_close -}; - -static int set_use_inc(void *data) -{ - return 0; -} - -static void set_use_dec(void *data) -{ -} - -static struct lirc_driver driver = { - .name = LIRC_DRIVER_NAME, - .minor = -1, - .code_length = 1, - .sample_rate = 0, - .data = NULL, - .add_to_buf = NULL, - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .fops = &lirc_fops, - .dev = NULL, - .owner = THIS_MODULE, -}; - -static struct platform_device *lirc_parallel_dev; - -static int __devinit lirc_parallel_probe(struct platform_device *dev) -{ - return 0; -} - -static int __devexit lirc_parallel_remove(struct platform_device *dev) -{ - return 0; -} - -static int lirc_parallel_suspend(struct platform_device *dev, - pm_message_t state) -{ - return 0; -} - -static int lirc_parallel_resume(struct platform_device *dev) -{ - return 0; -} - -static struct platform_driver lirc_parallel_driver = { - .probe = lirc_parallel_probe, - .remove = __devexit_p(lirc_parallel_remove), - .suspend = lirc_parallel_suspend, - .resume = lirc_parallel_resume, - .driver = { - .name = LIRC_DRIVER_NAME, - .owner = THIS_MODULE, - }, -}; - -static int pf(void *handle) -{ - parport_disable_irq(pport); - is_claimed = 0; - return 0; -} - -static void kf(void *handle) -{ - if (!is_open) - return; - if (!lirc_claim()) - return; - parport_enable_irq(pport); - lirc_off(); - /* this is a bit annoying when you actually print...*/ - /* - printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME); - */ -} - -/*** module initialization and cleanup ***/ - -static int __init lirc_parallel_init(void) -{ - int result; - - result = platform_driver_register(&lirc_parallel_driver); - if (result) { - printk(KERN_NOTICE "platform_driver_register" - " returned %d\n", result); - return result; - } - - lirc_parallel_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); - if (!lirc_parallel_dev) { - result = -ENOMEM; - goto exit_driver_unregister; - } - - result = platform_device_add(lirc_parallel_dev); - if (result) - goto exit_device_put; - - pport = parport_find_base(io); - if (pport == NULL) { - printk(KERN_NOTICE "%s: no port at %x found\n", - LIRC_DRIVER_NAME, io); - result = -ENXIO; - goto exit_device_put; - } - ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, - pf, kf, irq_handler, 0, NULL); - parport_put_port(pport); - if (ppdevice == NULL) { - printk(KERN_NOTICE "%s: parport_register_device() failed\n", - LIRC_DRIVER_NAME); - result = -ENXIO; - goto exit_device_put; - } - if (parport_claim(ppdevice) != 0) - goto skip_init; - is_claimed = 1; - out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); - -#ifdef LIRC_TIMER - if (debug) - out(LIRC_PORT_DATA, tx_mask); - - timer = init_lirc_timer(); - -#if 0 /* continue even if device is offline */ - if (timer == 0) { - is_claimed = 0; - parport_release(pport); - parport_unregister_device(ppdevice); - result = -EIO; - goto exit_device_put; - } - -#endif - if (debug) - out(LIRC_PORT_DATA, 0); -#endif - - is_claimed = 0; - parport_release(ppdevice); - skip_init: - driver.dev = &lirc_parallel_dev->dev; - driver.minor = lirc_register_driver(&driver); - if (driver.minor < 0) { - printk(KERN_NOTICE "%s: register_chrdev() failed\n", - LIRC_DRIVER_NAME); - parport_unregister_device(ppdevice); - result = -EIO; - goto exit_device_put; - } - printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", - LIRC_DRIVER_NAME, io, irq); - return 0; - -exit_device_put: - platform_device_put(lirc_parallel_dev); -exit_driver_unregister: - platform_driver_unregister(&lirc_parallel_driver); - return result; -} - -static void __exit lirc_parallel_exit(void) -{ - parport_unregister_device(ppdevice); - lirc_unregister_driver(driver.minor); - - platform_device_unregister(lirc_parallel_dev); - platform_driver_unregister(&lirc_parallel_driver); -} - -module_init(lirc_parallel_init); -module_exit(lirc_parallel_exit); - -MODULE_DESCRIPTION("Infrared receiver driver for parallel ports."); -MODULE_AUTHOR("Christoph Bartelmus"); -MODULE_LICENSE("GPL"); - -module_param(io, int, S_IRUGO); -MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)"); - -module_param(irq, int, S_IRUGO); -MODULE_PARM_DESC(irq, "Interrupt (7 or 5)"); - -module_param(tx_mask, int, S_IRUGO); -MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); - -module_param(check_pselecd, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Check for printer (default: 0)"); diff --git a/drivers/staging/lirc/lirc_parallel.h b/drivers/staging/lirc/lirc_parallel.h deleted file mode 100644 index 4bed6afe0632..000000000000 --- a/drivers/staging/lirc/lirc_parallel.h +++ /dev/null @@ -1,26 +0,0 @@ -/* lirc_parallel.h */ - -#ifndef _LIRC_PARALLEL_H -#define _LIRC_PARALLEL_H - -#include - -#define LIRC_PORT_LEN 3 - -#define LIRC_LP_BASE 0 -#define LIRC_LP_STATUS 1 -#define LIRC_LP_CONTROL 2 - -#define LIRC_PORT_DATA LIRC_LP_BASE /* base */ -#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */ -#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */ -#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */ -#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */ -#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */ - -#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */ - -#define LIRC_PARALLEL_MAX_TRANSMITTERS 8 -#define LIRC_PARALLEL_TRANSMITTER_MASK ((1< - * Tim Davies - * - * This driver was derived from: - * Venky Raju - * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD" - * Paul Miller 's 2003-2004 - * "lirc_atiusb - USB remote support for LIRC" - * Culver Consulting Services 's 2003 - * "Sasem OnAir VFD/IR USB driver" - * - * - * NOTE - The LCDproc iMon driver should work with this module. More info at - * http://www.frogstorm.info/sasem - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 - - -#define MOD_AUTHOR "Oliver Stabel , " \ - "Tim Davies " -#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1" -#define MOD_NAME "lirc_sasem" -#define MOD_VERSION "0.5" - -#define VFD_MINOR_BASE 144 /* Same as LCD */ -#define DEVICE_NAME "lcd%d" - -#define BUF_CHUNK_SIZE 8 -#define BUF_SIZE 128 - -#define IOCTL_LCD_CONTRAST 1 - -/*** P R O T O T Y P E S ***/ - -/* USB Callback prototypes */ -static int sasem_probe(struct usb_interface *interface, - const struct usb_device_id *id); -static void sasem_disconnect(struct usb_interface *interface); -static void usb_rx_callback(struct urb *urb); -static void usb_tx_callback(struct urb *urb); - -/* VFD file_operations function prototypes */ -static int vfd_open(struct inode *inode, struct file *file); -static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg); -static int vfd_close(struct inode *inode, struct file *file); -static ssize_t vfd_write(struct file *file, const char *buf, - size_t n_bytes, loff_t *pos); - -/* LIRC driver function prototypes */ -static int ir_open(void *data); -static void ir_close(void *data); - -/* Driver init/exit prototypes */ -static int __init sasem_init(void); -static void __exit sasem_exit(void); - -/*** G L O B A L S ***/ -#define SASEM_DATA_BUF_SZ 32 - -struct sasem_context { - - struct usb_device *dev; - int vfd_isopen; /* VFD port has been opened */ - unsigned int vfd_contrast; /* VFD contrast */ - int ir_isopen; /* IR port has been opened */ - int dev_present; /* USB device presence */ - struct mutex ctx_lock; /* to lock this object */ - wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ - - struct lirc_driver *driver; - struct usb_endpoint_descriptor *rx_endpoint; - struct usb_endpoint_descriptor *tx_endpoint; - struct urb *rx_urb; - struct urb *tx_urb; - unsigned char usb_rx_buf[8]; - unsigned char usb_tx_buf[8]; - - struct tx_t { - unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */ - struct completion finished; /* wait for write to finish */ - atomic_t busy; /* write in progress */ - int status; /* status of tx completion */ - } tx; - - /* for dealing with repeat codes (wish there was a toggle bit!) */ - struct timeval presstime; - char lastcode[8]; - int codesaved; -}; - -/* VFD file operations */ -static const struct file_operations vfd_fops = { - .owner = THIS_MODULE, - .open = &vfd_open, - .write = &vfd_write, - .unlocked_ioctl = &vfd_ioctl, - .release = &vfd_close, - .llseek = noop_llseek, -}; - -/* USB Device ID for Sasem USB Control Board */ -static struct usb_device_id sasem_usb_id_table[] = { - /* Sasem USB Control Board */ - { USB_DEVICE(0x11ba, 0x0101) }, - /* Terminating entry */ - {} -}; - -/* USB Device data */ -static struct usb_driver sasem_driver = { - .name = MOD_NAME, - .probe = sasem_probe, - .disconnect = sasem_disconnect, - .id_table = sasem_usb_id_table, -}; - -static struct usb_class_driver sasem_class = { - .name = DEVICE_NAME, - .fops = &vfd_fops, - .minor_base = VFD_MINOR_BASE, -}; - -/* to prevent races between open() and disconnect() */ -static DEFINE_MUTEX(disconnect_lock); - -static int debug; - - -/*** M O D U L E C O D E ***/ - -MODULE_AUTHOR(MOD_AUTHOR); -MODULE_DESCRIPTION(MOD_DESC); -MODULE_LICENSE("GPL"); -module_param(debug, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)"); - -static void delete_context(struct sasem_context *context) -{ - usb_free_urb(context->tx_urb); /* VFD */ - usb_free_urb(context->rx_urb); /* IR */ - lirc_buffer_free(context->driver->rbuf); - kfree(context->driver->rbuf); - kfree(context->driver); - kfree(context); - - if (debug) - printk(KERN_INFO "%s: context deleted\n", __func__); -} - -static void deregister_from_lirc(struct sasem_context *context) -{ - int retval; - int minor = context->driver->minor; - - retval = lirc_unregister_driver(minor); - if (retval) - err("%s: unable to deregister from lirc (%d)", - __func__, retval); - else - printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", - minor); - -} - -/** - * Called when the VFD device (e.g. /dev/usb/lcd) - * is opened by the application. - */ -static int vfd_open(struct inode *inode, struct file *file) -{ - struct usb_interface *interface; - struct sasem_context *context = NULL; - int subminor; - int retval = 0; - - /* prevent races with disconnect */ - mutex_lock(&disconnect_lock); - - subminor = iminor(inode); - interface = usb_find_interface(&sasem_driver, subminor); - if (!interface) { - err("%s: could not find interface for minor %d", - __func__, subminor); - retval = -ENODEV; - goto exit; - } - context = usb_get_intfdata(interface); - - if (!context) { - err("%s: no context found for minor %d", - __func__, subminor); - retval = -ENODEV; - goto exit; - } - - mutex_lock(&context->ctx_lock); - - if (context->vfd_isopen) { - err("%s: VFD port is already open", __func__); - retval = -EBUSY; - } else { - context->vfd_isopen = 1; - file->private_data = context; - printk(KERN_INFO "VFD port opened\n"); - } - - mutex_unlock(&context->ctx_lock); - -exit: - mutex_unlock(&disconnect_lock); - return retval; -} - -/** - * Called when the VFD device (e.g. /dev/usb/lcd) - * is closed by the application. - */ -static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) -{ - struct sasem_context *context = NULL; - - context = (struct sasem_context *) file->private_data; - - if (!context) { - err("%s: no context for device", __func__); - return -ENODEV; - } - - mutex_lock(&context->ctx_lock); - - switch (cmd) { - case IOCTL_LCD_CONTRAST: - if (arg > 1000) - arg = 1000; - context->vfd_contrast = (unsigned int)arg; - break; - default: - printk(KERN_INFO "Unknown IOCTL command\n"); - mutex_unlock(&context->ctx_lock); - return -ENOIOCTLCMD; /* not supported */ - } - - mutex_unlock(&context->ctx_lock); - return 0; -} - -/** - * Called when the VFD device (e.g. /dev/usb/lcd) - * is closed by the application. - */ -static int vfd_close(struct inode *inode, struct file *file) -{ - struct sasem_context *context = NULL; - int retval = 0; - - context = (struct sasem_context *) file->private_data; - - if (!context) { - err("%s: no context for device", __func__); - return -ENODEV; - } - - mutex_lock(&context->ctx_lock); - - if (!context->vfd_isopen) { - err("%s: VFD is not open", __func__); - retval = -EIO; - } else { - context->vfd_isopen = 0; - printk(KERN_INFO "VFD port closed\n"); - if (!context->dev_present && !context->ir_isopen) { - - /* Device disconnected before close and IR port is - * not open. If IR port is open, context will be - * deleted by ir_close. */ - mutex_unlock(&context->ctx_lock); - delete_context(context); - return retval; - } - } - - mutex_unlock(&context->ctx_lock); - return retval; -} - -/** - * Sends a packet to the VFD. - */ -static int send_packet(struct sasem_context *context) -{ - unsigned int pipe; - int interval = 0; - int retval = 0; - - pipe = usb_sndintpipe(context->dev, - context->tx_endpoint->bEndpointAddress); - interval = context->tx_endpoint->bInterval; - - usb_fill_int_urb(context->tx_urb, context->dev, pipe, - context->usb_tx_buf, sizeof(context->usb_tx_buf), - usb_tx_callback, context, interval); - - context->tx_urb->actual_length = 0; - - init_completion(&context->tx.finished); - atomic_set(&(context->tx.busy), 1); - - retval = usb_submit_urb(context->tx_urb, GFP_KERNEL); - if (retval) { - atomic_set(&(context->tx.busy), 0); - err("%s: error submitting urb (%d)", __func__, retval); - } else { - /* Wait for transmission to complete (or abort) */ - mutex_unlock(&context->ctx_lock); - wait_for_completion(&context->tx.finished); - mutex_lock(&context->ctx_lock); - - retval = context->tx.status; - if (retval) - err("%s: packet tx failed (%d)", __func__, retval); - } - - return retval; -} - -/** - * Writes data to the VFD. The Sasem VFD is 2x16 characters - * and requires data in 9 consecutive USB interrupt packets, - * each packet carrying 8 bytes. - */ -static ssize_t vfd_write(struct file *file, const char *buf, - size_t n_bytes, loff_t *pos) -{ - int i; - int retval = 0; - struct sasem_context *context; - int *data_buf = NULL; - - context = (struct sasem_context *) file->private_data; - if (!context) { - err("%s: no context for device", __func__); - return -ENODEV; - } - - mutex_lock(&context->ctx_lock); - - if (!context->dev_present) { - err("%s: no Sasem device present", __func__); - retval = -ENODEV; - goto exit; - } - - if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) { - err("%s: invalid payload size", __func__); - retval = -EINVAL; - goto exit; - } - - data_buf = memdup_user(buf, n_bytes); - if (IS_ERR(data_buf)) { - retval = PTR_ERR(data_buf); - goto exit; - } - - memcpy(context->tx.data_buf, data_buf, n_bytes); - - /* Pad with spaces */ - for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i) - context->tx.data_buf[i] = ' '; - - /* Nine 8 byte packets to be sent */ - /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0" - * will clear the VFD */ - for (i = 0; i < 9; i++) { - switch (i) { - case 0: - memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8); - context->usb_tx_buf[1] = (context->vfd_contrast) ? - (0x2B - (context->vfd_contrast - 1) / 250) - : 0x2B; - break; - case 1: - memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); - break; - case 2: - memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8); - break; - case 3: - memcpy(context->usb_tx_buf, context->tx.data_buf, 8); - break; - case 4: - memcpy(context->usb_tx_buf, - context->tx.data_buf + 8, 8); - break; - case 5: - memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); - break; - case 6: - memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8); - break; - case 7: - memcpy(context->usb_tx_buf, - context->tx.data_buf + 16, 8); - break; - case 8: - memcpy(context->usb_tx_buf, - context->tx.data_buf + 24, 8); - break; - } - retval = send_packet(context); - if (retval) { - - err("%s: send packet failed for packet #%d", - __func__, i); - goto exit; - } - } -exit: - - mutex_unlock(&context->ctx_lock); - kfree(data_buf); - - return (!retval) ? n_bytes : retval; -} - -/** - * Callback function for USB core API: transmit data - */ -static void usb_tx_callback(struct urb *urb) -{ - struct sasem_context *context; - - if (!urb) - return; - context = (struct sasem_context *) urb->context; - if (!context) - return; - - context->tx.status = urb->status; - - /* notify waiters that write has finished */ - atomic_set(&context->tx.busy, 0); - complete(&context->tx.finished); - - return; -} - -/** - * Called by lirc_dev when the application opens /dev/lirc - */ -static int ir_open(void *data) -{ - int retval = 0; - struct sasem_context *context; - - /* prevent races with disconnect */ - mutex_lock(&disconnect_lock); - - context = (struct sasem_context *) data; - - mutex_lock(&context->ctx_lock); - - if (context->ir_isopen) { - err("%s: IR port is already open", __func__); - retval = -EBUSY; - goto exit; - } - - usb_fill_int_urb(context->rx_urb, context->dev, - usb_rcvintpipe(context->dev, - context->rx_endpoint->bEndpointAddress), - context->usb_rx_buf, sizeof(context->usb_rx_buf), - usb_rx_callback, context, context->rx_endpoint->bInterval); - - retval = usb_submit_urb(context->rx_urb, GFP_KERNEL); - - if (retval) - err("%s: usb_submit_urb failed for ir_open (%d)", - __func__, retval); - else { - context->ir_isopen = 1; - printk(KERN_INFO "IR port opened\n"); - } - -exit: - mutex_unlock(&context->ctx_lock); - - mutex_unlock(&disconnect_lock); - return retval; -} - -/** - * Called by lirc_dev when the application closes /dev/lirc - */ -static void ir_close(void *data) -{ - struct sasem_context *context; - - context = (struct sasem_context *)data; - if (!context) { - err("%s: no context for device", __func__); - return; - } - - mutex_lock(&context->ctx_lock); - - usb_kill_urb(context->rx_urb); - context->ir_isopen = 0; - printk(KERN_INFO "IR port closed\n"); - - if (!context->dev_present) { - - /* - * Device disconnected while IR port was - * still open. Driver was not deregistered - * at disconnect time, so do it now. - */ - deregister_from_lirc(context); - - if (!context->vfd_isopen) { - - mutex_unlock(&context->ctx_lock); - delete_context(context); - return; - } - /* If VFD port is open, context will be deleted by vfd_close */ - } - - mutex_unlock(&context->ctx_lock); - return; -} - -/** - * Process the incoming packet - */ -static void incoming_packet(struct sasem_context *context, - struct urb *urb) -{ - int len = urb->actual_length; - unsigned char *buf = urb->transfer_buffer; - long ms; - struct timeval tv; - int i; - - if (len != 8) { - printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", - __func__, len); - return; - } - - if (debug) { - printk(KERN_INFO "Incoming data: "); - for (i = 0; i < 8; ++i) - printk(KERN_CONT "%02x ", buf[i]); - printk(KERN_CONT "\n"); - } - - /* - * Lirc could deal with the repeat code, but we really need to block it - * if it arrives too late. Otherwise we could repeat the wrong code. - */ - - /* get the time since the last button press */ - do_gettimeofday(&tv); - ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 + - (tv.tv_usec - context->presstime.tv_usec) / 1000; - - if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) { - /* - * the repeat code is being sent, so we copy - * the old code to LIRC - */ - - /* - * NOTE: Only if the last code was less than 250ms ago - * - no one should be able to push another (undetected) button - * in that time and then get a false repeat of the previous - * press but it is long enough for a genuine repeat - */ - if ((ms < 250) && (context->codesaved != 0)) { - memcpy(buf, &context->lastcode, 8); - context->presstime.tv_sec = tv.tv_sec; - context->presstime.tv_usec = tv.tv_usec; - } - } else { - /* save the current valid code for repeats */ - memcpy(&context->lastcode, buf, 8); - /* - * set flag to signal a valid code was save; - * just for safety reasons - */ - context->codesaved = 1; - context->presstime.tv_sec = tv.tv_sec; - context->presstime.tv_usec = tv.tv_usec; - } - - lirc_buffer_write(context->driver->rbuf, buf); - wake_up(&context->driver->rbuf->wait_poll); -} - -/** - * Callback function for USB core API: receive data - */ -static void usb_rx_callback(struct urb *urb) -{ - struct sasem_context *context; - - if (!urb) - return; - context = (struct sasem_context *) urb->context; - if (!context) - return; - - switch (urb->status) { - - case -ENOENT: /* usbcore unlink successful! */ - return; - - case 0: - if (context->ir_isopen) - incoming_packet(context, urb); - break; - - default: - printk(KERN_WARNING "%s: status (%d): ignored", - __func__, urb->status); - break; - } - - usb_submit_urb(context->rx_urb, GFP_ATOMIC); - return; -} - - - -/** - * Callback function for USB core API: Probe - */ -static int sasem_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct usb_device *dev = NULL; - struct usb_host_interface *iface_desc = NULL; - struct usb_endpoint_descriptor *rx_endpoint = NULL; - struct usb_endpoint_descriptor *tx_endpoint = NULL; - struct urb *rx_urb = NULL; - struct urb *tx_urb = NULL; - struct lirc_driver *driver = NULL; - struct lirc_buffer *rbuf = NULL; - int lirc_minor = 0; - int num_endpoints; - int retval = 0; - int vfd_ep_found; - int ir_ep_found; - int alloc_status; - struct sasem_context *context = NULL; - int i; - - printk(KERN_INFO "%s: found Sasem device\n", __func__); - - - dev = usb_get_dev(interface_to_usbdev(interface)); - iface_desc = interface->cur_altsetting; - num_endpoints = iface_desc->desc.bNumEndpoints; - - /* - * Scan the endpoint list and set: - * first input endpoint = IR endpoint - * first output endpoint = VFD endpoint - */ - - ir_ep_found = 0; - vfd_ep_found = 0; - - for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) { - - struct usb_endpoint_descriptor *ep; - int ep_dir; - int ep_type; - ep = &iface_desc->endpoint [i].desc; - ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; - ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; - - if (!ir_ep_found && - ep_dir == USB_DIR_IN && - ep_type == USB_ENDPOINT_XFER_INT) { - - rx_endpoint = ep; - ir_ep_found = 1; - if (debug) - printk(KERN_INFO "%s: found IR endpoint\n", - __func__); - - } else if (!vfd_ep_found && - ep_dir == USB_DIR_OUT && - ep_type == USB_ENDPOINT_XFER_INT) { - - tx_endpoint = ep; - vfd_ep_found = 1; - if (debug) - printk(KERN_INFO "%s: found VFD endpoint\n", - __func__); - } - } - - /* Input endpoint is mandatory */ - if (!ir_ep_found) { - - err("%s: no valid input (IR) endpoint found.", __func__); - retval = -ENODEV; - goto exit; - } - - if (!vfd_ep_found) - printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n", - __func__); - - - /* Allocate memory */ - alloc_status = 0; - - context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL); - if (!context) { - err("%s: kzalloc failed for context", __func__); - alloc_status = 1; - goto alloc_status_switch; - } - driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); - if (!driver) { - err("%s: kzalloc failed for lirc_driver", __func__); - alloc_status = 2; - goto alloc_status_switch; - } - rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!rbuf) { - err("%s: kmalloc failed for lirc_buffer", __func__); - alloc_status = 3; - goto alloc_status_switch; - } - if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { - err("%s: lirc_buffer_init failed", __func__); - alloc_status = 4; - goto alloc_status_switch; - } - rx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!rx_urb) { - err("%s: usb_alloc_urb failed for IR urb", __func__); - alloc_status = 5; - goto alloc_status_switch; - } - if (vfd_ep_found) { - tx_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!tx_urb) { - err("%s: usb_alloc_urb failed for VFD urb", - __func__); - alloc_status = 6; - goto alloc_status_switch; - } - } - - mutex_init(&context->ctx_lock); - - strcpy(driver->name, MOD_NAME); - driver->minor = -1; - driver->code_length = 64; - driver->sample_rate = 0; - driver->features = LIRC_CAN_REC_LIRCCODE; - driver->data = context; - driver->rbuf = rbuf; - driver->set_use_inc = ir_open; - driver->set_use_dec = ir_close; - driver->dev = &interface->dev; - driver->owner = THIS_MODULE; - - mutex_lock(&context->ctx_lock); - - lirc_minor = lirc_register_driver(driver); - if (lirc_minor < 0) { - err("%s: lirc_register_driver failed", __func__); - alloc_status = 7; - retval = lirc_minor; - goto unlock; - } else - printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", - __func__, lirc_minor); - - /* Needed while unregistering! */ - driver->minor = lirc_minor; - - context->dev = dev; - context->dev_present = 1; - context->rx_endpoint = rx_endpoint; - context->rx_urb = rx_urb; - if (vfd_ep_found) { - context->tx_endpoint = tx_endpoint; - context->tx_urb = tx_urb; - context->vfd_contrast = 1000; /* range 0 - 1000 */ - } - context->driver = driver; - - usb_set_intfdata(interface, context); - - if (vfd_ep_found) { - - if (debug) - printk(KERN_INFO "Registering VFD with sysfs\n"); - if (usb_register_dev(interface, &sasem_class)) - /* Not a fatal error, so ignore */ - printk(KERN_INFO "%s: could not get a minor number " - "for VFD\n", __func__); - } - - printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", - __func__, dev->bus->busnum, dev->devnum); -unlock: - mutex_unlock(&context->ctx_lock); - -alloc_status_switch: - switch (alloc_status) { - - case 7: - if (vfd_ep_found) - usb_free_urb(tx_urb); - case 6: - usb_free_urb(rx_urb); - case 5: - lirc_buffer_free(rbuf); - case 4: - kfree(rbuf); - case 3: - kfree(driver); - case 2: - kfree(context); - context = NULL; - case 1: - if (retval == 0) - retval = -ENOMEM; - } - -exit: - return retval; -} - -/** - * Callback function for USB core API: disonnect - */ -static void sasem_disconnect(struct usb_interface *interface) -{ - struct sasem_context *context; - - /* prevent races with ir_open()/vfd_open() */ - mutex_lock(&disconnect_lock); - - context = usb_get_intfdata(interface); - mutex_lock(&context->ctx_lock); - - printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); - - usb_set_intfdata(interface, NULL); - context->dev_present = 0; - - /* Stop reception */ - usb_kill_urb(context->rx_urb); - - /* Abort ongoing write */ - if (atomic_read(&context->tx.busy)) { - - usb_kill_urb(context->tx_urb); - wait_for_completion(&context->tx.finished); - } - - /* De-register from lirc_dev if IR port is not open */ - if (!context->ir_isopen) - deregister_from_lirc(context); - - usb_deregister_dev(interface, &sasem_class); - - mutex_unlock(&context->ctx_lock); - - if (!context->ir_isopen && !context->vfd_isopen) - delete_context(context); - - mutex_unlock(&disconnect_lock); -} - -static int __init sasem_init(void) -{ - int rc; - - printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n"); - printk(KERN_INFO MOD_AUTHOR "\n"); - - rc = usb_register(&sasem_driver); - if (rc < 0) { - err("%s: usb register failed (%d)", __func__, rc); - return -ENODEV; - } - return 0; -} - -static void __exit sasem_exit(void) -{ - usb_deregister(&sasem_driver); - printk(KERN_INFO "module removed. Goodbye!\n"); -} - - -module_init(sasem_init); -module_exit(sasem_exit); diff --git a/drivers/staging/lirc/lirc_serial.c b/drivers/staging/lirc/lirc_serial.c deleted file mode 100644 index 8a060a8a7224..000000000000 --- a/drivers/staging/lirc/lirc_serial.c +++ /dev/null @@ -1,1315 +0,0 @@ -/* - * lirc_serial.c - * - * lirc_serial - Device driver that records pulse- and pause-lengths - * (space-lengths) between DDCD event on a serial port. - * - * Copyright (C) 1996,97 Ralph Metzler - * Copyright (C) 1998 Trent Piepho - * Copyright (C) 1998 Ben Pfaff - * Copyright (C) 1999 Christoph Bartelmus - * Copyright (C) 2007 Andrei Tanas (suspend/resume 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -/* - * Steve's changes to improve transmission fidelity: - * - for systems with the rdtsc instruction and the clock counter, a - * send_pule that times the pulses directly using the counter. - * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is - * not needed. Measurement shows very stable waveform, even where - * PCI activity slows the access to the UART, which trips up other - * versions. - * - For other system, non-integer-microsecond pulse/space lengths, - * done using fixed point binary. So, much more accurate carrier - * frequency. - * - fine tuned transmitter latency, taking advantage of fractional - * microseconds in previous change - * - Fixed bug in the way transmitter latency was accounted for by - * tuning the pulse lengths down - the send_pulse routine ignored - * this overhead as it timed the overall pulse length - so the - * pulse frequency was right but overall pulse length was too - * long. Fixed by accounting for latency on each pulse/space - * iteration. - * - * Steve Davies July 2001 - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#ifdef CONFIG_LIRC_SERIAL_NSLU2 -#include -#endif -/* From Intel IXP42X Developer's Manual (#252480-005): */ -/* ftp://download.intel.com/design/network/manuals/25248005.pdf */ -#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */ -#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */ - -#include -#include - -#define LIRC_DRIVER_NAME "lirc_serial" - -struct lirc_serial { - int signal_pin; - int signal_pin_change; - u8 on; - u8 off; - long (*send_pulse)(unsigned long length); - void (*send_space)(long length); - int features; - spinlock_t lock; -}; - -#define LIRC_HOMEBREW 0 -#define LIRC_IRDEO 1 -#define LIRC_IRDEO_REMOTE 2 -#define LIRC_ANIMAX 3 -#define LIRC_IGOR 4 -#define LIRC_NSLU2 5 - -/*** module parameters ***/ -static int type; -static int io; -static int irq; -static int iommap; -static int ioshift; -static int softcarrier = 1; -static int share_irq; -static int debug; -static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */ -static int txsense; /* 0 = active high, 1 = active low */ - -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ - fmt, ## args); \ - } while (0) - -/* forward declarations */ -static long send_pulse_irdeo(unsigned long length); -static long send_pulse_homebrew(unsigned long length); -static void send_space_irdeo(long length); -static void send_space_homebrew(long length); - -static struct lirc_serial hardware[] = { - [LIRC_HOMEBREW] = { - .signal_pin = UART_MSR_DCD, - .signal_pin_change = UART_MSR_DDCD, - .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), - .off = (UART_MCR_RTS | UART_MCR_OUT2), - .send_pulse = send_pulse_homebrew, - .send_space = send_space_homebrew, -#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | - LIRC_CAN_SET_SEND_CARRIER | - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) -#else - .features = LIRC_CAN_REC_MODE2 -#endif - }, - - [LIRC_IRDEO] = { - .signal_pin = UART_MSR_DSR, - .signal_pin_change = UART_MSR_DDSR, - .on = UART_MCR_OUT2, - .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), - .send_pulse = send_pulse_irdeo, - .send_space = send_space_irdeo, - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) - }, - - [LIRC_IRDEO_REMOTE] = { - .signal_pin = UART_MSR_DSR, - .signal_pin_change = UART_MSR_DDSR, - .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), - .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), - .send_pulse = send_pulse_irdeo, - .send_space = send_space_irdeo, - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) - }, - - [LIRC_ANIMAX] = { - .signal_pin = UART_MSR_DCD, - .signal_pin_change = UART_MSR_DDCD, - .on = 0, - .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), - .send_pulse = NULL, - .send_space = NULL, - .features = LIRC_CAN_REC_MODE2 - }, - - [LIRC_IGOR] = { - .signal_pin = UART_MSR_DSR, - .signal_pin_change = UART_MSR_DDSR, - .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), - .off = (UART_MCR_RTS | UART_MCR_OUT2), - .send_pulse = send_pulse_homebrew, - .send_space = send_space_homebrew, -#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | - LIRC_CAN_SET_SEND_CARRIER | - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) -#else - .features = LIRC_CAN_REC_MODE2 -#endif - }, - -#ifdef CONFIG_LIRC_SERIAL_NSLU2 - /* - * Modified Linksys Network Storage Link USB 2.0 (NSLU2): - * We receive on CTS of the 2nd serial port (R142,LHS), we - * transmit with a IR diode between GPIO[1] (green status LED), - * and ground (Matthias Goebl ). - * See also http://www.nslu2-linux.org for this device - */ - [LIRC_NSLU2] = { - .signal_pin = UART_MSR_CTS, - .signal_pin_change = UART_MSR_DCTS, - .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), - .off = (UART_MCR_RTS | UART_MCR_OUT2), - .send_pulse = send_pulse_homebrew, - .send_space = send_space_homebrew, -#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER - .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | - LIRC_CAN_SET_SEND_CARRIER | - LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) -#else - .features = LIRC_CAN_REC_MODE2 -#endif - }, -#endif - -}; - -#define RS_ISR_PASS_LIMIT 256 - -/* - * A long pulse code from a remote might take up to 300 bytes. The - * daemon should read the bytes as soon as they are generated, so take - * the number of keys you think you can push before the daemon runs - * and multiply by 300. The driver will warn you if you overrun this - * buffer. If you have a slow computer or non-busmastering IDE disks, - * maybe you will need to increase this. - */ - -/* This MUST be a power of two! It has to be larger than 1 as well. */ - -#define RBUF_LEN 256 - -static struct timeval lasttv = {0, 0}; - -static struct lirc_buffer rbuf; - -static unsigned int freq = 38000; -static unsigned int duty_cycle = 50; - -/* Initialized in init_timing_params() */ -static unsigned long period; -static unsigned long pulse_width; -static unsigned long space_width; - -#if defined(__i386__) -/* - * From: - * Linux I/O port programming mini-HOWTO - * Author: Riku Saikkonen - * v, 28 December 1997 - * - * [...] - * Actually, a port I/O instruction on most ports in the 0-0x3ff range - * takes almost exactly 1 microsecond, so if you're, for example, using - * the parallel port directly, just do additional inb()s from that port - * to delay. - * [...] - */ -/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from - * comment above plus trimming to match actual measured frequency. - * This will be sensitive to cpu speed, though hopefully most of the 1.5us - * is spent in the uart access. Still - for reference test machine was a - * 1.13GHz Athlon system - Steve - */ - -/* - * changed from 400 to 450 as this works better on slower machines; - * faster machines will use the rdtsc code anyway - */ -#define LIRC_SERIAL_TRANSMITTER_LATENCY 450 - -#else - -/* does anybody have information on other platforms ? */ -/* 256 = 1<<8 */ -#define LIRC_SERIAL_TRANSMITTER_LATENCY 256 - -#endif /* __i386__ */ -/* - * FIXME: should we be using hrtimers instead of this - * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense? - */ - -/* fetch serial input packet (1 byte) from register offset */ -static u8 sinp(int offset) -{ - if (iommap != 0) - /* the register is memory-mapped */ - offset <<= ioshift; - - return inb(io + offset); -} - -/* write serial output packet (1 byte) of value to register offset */ -static void soutp(int offset, u8 value) -{ - if (iommap != 0) - /* the register is memory-mapped */ - offset <<= ioshift; - - outb(value, io + offset); -} - -static void on(void) -{ -#ifdef CONFIG_LIRC_SERIAL_NSLU2 - /* - * On NSLU2, we put the transmit diode between the output of the green - * status LED and ground - */ - if (type == LIRC_NSLU2) { - gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW); - return; - } -#endif - if (txsense) - soutp(UART_MCR, hardware[type].off); - else - soutp(UART_MCR, hardware[type].on); -} - -static void off(void) -{ -#ifdef CONFIG_LIRC_SERIAL_NSLU2 - if (type == LIRC_NSLU2) { - gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH); - return; - } -#endif - if (txsense) - soutp(UART_MCR, hardware[type].on); - else - soutp(UART_MCR, hardware[type].off); -} - -#ifndef MAX_UDELAY_MS -#define MAX_UDELAY_US 5000 -#else -#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) -#endif - -static void safe_udelay(unsigned long usecs) -{ - while (usecs > MAX_UDELAY_US) { - udelay(MAX_UDELAY_US); - usecs -= MAX_UDELAY_US; - } - udelay(usecs); -} - -#ifdef USE_RDTSC -/* - * This is an overflow/precision juggle, complicated in that we can't - * do long long divide in the kernel - */ - -/* - * When we use the rdtsc instruction to measure clocks, we keep the - * pulse and space widths as clock cycles. As this is CPU speed - * dependent, the widths must be calculated in init_port and ioctl - * time - */ - -/* So send_pulse can quickly convert microseconds to clocks */ -static unsigned long conv_us_to_clocks; - -static int init_timing_params(unsigned int new_duty_cycle, - unsigned int new_freq) -{ - __u64 loops_per_sec, work; - - duty_cycle = new_duty_cycle; - freq = new_freq; - - loops_per_sec = __this_cpu_read(cpu.info.loops_per_jiffy); - loops_per_sec *= HZ; - - /* How many clocks in a microsecond?, avoiding long long divide */ - work = loops_per_sec; - work *= 4295; /* 4295 = 2^32 / 1e6 */ - conv_us_to_clocks = (work >> 32); - - /* - * Carrier period in clocks, approach good up to 32GHz clock, - * gets carrier frequency within 8Hz - */ - period = loops_per_sec >> 3; - period /= (freq >> 3); - - /* Derive pulse and space from the period */ - pulse_width = period * duty_cycle / 100; - space_width = period - pulse_width; - dprintk("in init_timing_params, freq=%d, duty_cycle=%d, " - "clk/jiffy=%ld, pulse=%ld, space=%ld, " - "conv_us_to_clocks=%ld\n", - freq, duty_cycle, __this_cpu_read(cpu_info.loops_per_jiffy), - pulse_width, space_width, conv_us_to_clocks); - return 0; -} -#else /* ! USE_RDTSC */ -static int init_timing_params(unsigned int new_duty_cycle, - unsigned int new_freq) -{ -/* - * period, pulse/space width are kept with 8 binary places - - * IE multiplied by 256. - */ - if (256 * 1000000L / new_freq * new_duty_cycle / 100 <= - LIRC_SERIAL_TRANSMITTER_LATENCY) - return -EINVAL; - if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= - LIRC_SERIAL_TRANSMITTER_LATENCY) - return -EINVAL; - duty_cycle = new_duty_cycle; - freq = new_freq; - period = 256 * 1000000L / freq; - pulse_width = period * duty_cycle / 100; - space_width = period - pulse_width; - dprintk("in init_timing_params, freq=%d pulse=%ld, " - "space=%ld\n", freq, pulse_width, space_width); - return 0; -} -#endif /* USE_RDTSC */ - - -/* return value: space length delta */ - -static long send_pulse_irdeo(unsigned long length) -{ - long rawbits, ret; - int i; - unsigned char output; - unsigned char chunk, shifted; - - /* how many bits have to be sent ? */ - rawbits = length * 1152 / 10000; - if (duty_cycle > 50) - chunk = 3; - else - chunk = 1; - for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) { - shifted = chunk << (i * 3); - shifted >>= 1; - output &= (~shifted); - i++; - if (i == 3) { - soutp(UART_TX, output); - while (!(sinp(UART_LSR) & UART_LSR_THRE)) - ; - output = 0x7f; - i = 0; - } - } - if (i != 0) { - soutp(UART_TX, output); - while (!(sinp(UART_LSR) & UART_LSR_TEMT)) - ; - } - - if (i == 0) - ret = (-rawbits) * 10000 / 1152; - else - ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152; - - return ret; -} - -#ifdef USE_RDTSC -/* Version that uses Pentium rdtsc instruction to measure clocks */ - -/* - * This version does sub-microsecond timing using rdtsc instruction, - * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY - * Implicitly i586 architecture... - Steve - */ - -static long send_pulse_homebrew_softcarrier(unsigned long length) -{ - int flag; - unsigned long target, start, now; - - /* Get going quick as we can */ - rdtscl(start); - on(); - /* Convert length from microseconds to clocks */ - length *= conv_us_to_clocks; - /* And loop till time is up - flipping at right intervals */ - now = start; - target = pulse_width; - flag = 1; - /* - * FIXME: This looks like a hard busy wait, without even an occasional, - * polite, cpu_relax() call. There's got to be a better way? - * - * The i2c code has the result of a lot of bit-banging work, I wonder if - * there's something there which could be helpful here. - */ - while ((now - start) < length) { - /* Delay till flip time */ - do { - rdtscl(now); - } while ((now - start) < target); - - /* flip */ - if (flag) { - rdtscl(now); - off(); - target += space_width; - } else { - rdtscl(now); on(); - target += pulse_width; - } - flag = !flag; - } - rdtscl(now); - return ((now - start) - length) / conv_us_to_clocks; -} -#else /* ! USE_RDTSC */ -/* Version using udelay() */ - -/* - * here we use fixed point arithmetic, with 8 - * fractional bits. that gets us within 0.1% or so of the right average - * frequency, albeit with some jitter in pulse length - Steve - */ - -/* To match 8 fractional bits used for pulse/space length */ - -static long send_pulse_homebrew_softcarrier(unsigned long length) -{ - int flag; - unsigned long actual, target, d; - length <<= 8; - - actual = 0; target = 0; flag = 0; - while (actual < length) { - if (flag) { - off(); - target += space_width; - } else { - on(); - target += pulse_width; - } - d = (target - actual - - LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8; - /* - * Note - we've checked in ioctl that the pulse/space - * widths are big enough so that d is > 0 - */ - udelay(d); - actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY; - flag = !flag; - } - return (actual-length) >> 8; -} -#endif /* USE_RDTSC */ - -static long send_pulse_homebrew(unsigned long length) -{ - if (length <= 0) - return 0; - - if (softcarrier) - return send_pulse_homebrew_softcarrier(length); - else { - on(); - safe_udelay(length); - return 0; - } -} - -static void send_space_irdeo(long length) -{ - if (length <= 0) - return; - - safe_udelay(length); -} - -static void send_space_homebrew(long length) -{ - off(); - if (length <= 0) - return; - safe_udelay(length); -} - -static void rbwrite(int l) -{ - if (lirc_buffer_full(&rbuf)) { - /* no new signals will be accepted */ - dprintk("Buffer overrun\n"); - return; - } - lirc_buffer_write(&rbuf, (void *)&l); -} - -static void frbwrite(int l) -{ - /* simple noise filter */ - static int pulse, space; - static unsigned int ptr; - - if (ptr > 0 && (l & PULSE_BIT)) { - pulse += l & PULSE_MASK; - if (pulse > 250) { - rbwrite(space); - rbwrite(pulse | PULSE_BIT); - ptr = 0; - pulse = 0; - } - return; - } - if (!(l & PULSE_BIT)) { - if (ptr == 0) { - if (l > 20000) { - space = l; - ptr++; - return; - } - } else { - if (l > 20000) { - space += pulse; - if (space > PULSE_MASK) - space = PULSE_MASK; - space += l; - if (space > PULSE_MASK) - space = PULSE_MASK; - pulse = 0; - return; - } - rbwrite(space); - rbwrite(pulse | PULSE_BIT); - ptr = 0; - pulse = 0; - } - } - rbwrite(l); -} - -static irqreturn_t irq_handler(int i, void *blah) -{ - struct timeval tv; - int counter, dcd; - u8 status; - long deltv; - int data; - static int last_dcd = -1; - - if ((sinp(UART_IIR) & UART_IIR_NO_INT)) { - /* not our interrupt */ - return IRQ_NONE; - } - - counter = 0; - do { - counter++; - status = sinp(UART_MSR); - if (counter > RS_ISR_PASS_LIMIT) { - printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: " - "We're caught!\n"); - break; - } - if ((status & hardware[type].signal_pin_change) - && sense != -1) { - /* get current time */ - do_gettimeofday(&tv); - - /* New mode, written by Trent Piepho - . */ - - /* - * The old format was not very portable. - * We now use an int to pass pulses - * and spaces to user space. - * - * If PULSE_BIT is set a pulse has been - * received, otherwise a space has been - * received. The driver needs to know if your - * receiver is active high or active low, or - * the space/pulse sense could be - * inverted. The bits denoted by PULSE_MASK are - * the length in microseconds. Lengths greater - * than or equal to 16 seconds are clamped to - * PULSE_MASK. All other bits are unused. - * This is a much simpler interface for user - * programs, as well as eliminating "out of - * phase" errors with space/pulse - * autodetection. - */ - - /* calc time since last interrupt in microseconds */ - dcd = (status & hardware[type].signal_pin) ? 1 : 0; - - if (dcd == last_dcd) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": ignoring spike: %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); - continue; - } - - deltv = tv.tv_sec-lasttv.tv_sec; - if (tv.tv_sec < lasttv.tv_sec || - (tv.tv_sec == lasttv.tv_sec && - tv.tv_usec < lasttv.tv_usec)) { - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: your clock just jumped " - "backwards\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": %d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); - data = PULSE_MASK; - } else if (deltv > 15) { - data = PULSE_MASK; /* really long time */ - if (!(dcd^sense)) { - /* sanity check */ - printk(KERN_WARNING LIRC_DRIVER_NAME - ": AIEEEE: " - "%d %d %lx %lx %lx %lx\n", - dcd, sense, - tv.tv_sec, lasttv.tv_sec, - tv.tv_usec, lasttv.tv_usec); - /* - * detecting pulse while this - * MUST be a space! - */ - sense = sense ? 0 : 1; - } - } else - data = (int) (deltv*1000000 + - tv.tv_usec - - lasttv.tv_usec); - frbwrite(dcd^sense ? data : (data|PULSE_BIT)); - lasttv = tv; - last_dcd = dcd; - wake_up_interruptible(&rbuf.wait_poll); - } - } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */ - return IRQ_HANDLED; -} - - -static int hardware_init_port(void) -{ - u8 scratch, scratch2, scratch3; - - /* - * This is a simple port existence test, borrowed from the autoconfig - * function in drivers/serial/8250.c - */ - scratch = sinp(UART_IER); - soutp(UART_IER, 0); -#ifdef __i386__ - outb(0xff, 0x080); -#endif - scratch2 = sinp(UART_IER) & 0x0f; - soutp(UART_IER, 0x0f); -#ifdef __i386__ - outb(0x00, 0x080); -#endif - scratch3 = sinp(UART_IER) & 0x0f; - soutp(UART_IER, scratch); - if (scratch2 != 0 || scratch3 != 0x0f) { - /* we fail, there's nothing here */ - printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test " - "failed, cannot continue\n"); - return -EINVAL; - } - - - - /* Set DLAB 0. */ - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); - - /* First of all, disable all interrupts */ - soutp(UART_IER, sinp(UART_IER) & - (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); - - /* Clear registers. */ - sinp(UART_LSR); - sinp(UART_RX); - sinp(UART_IIR); - sinp(UART_MSR); - -#ifdef CONFIG_LIRC_SERIAL_NSLU2 - if (type == LIRC_NSLU2) { - /* Setup NSLU2 UART */ - - /* Enable UART */ - soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE); - /* Disable Receiver data Time out interrupt */ - soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE); - /* set out2 = interrupt unmask; off() doesn't set MCR - on NSLU2 */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); - } -#endif - - /* Set line for power source */ - off(); - - /* Clear registers again to be sure. */ - sinp(UART_LSR); - sinp(UART_RX); - sinp(UART_IIR); - sinp(UART_MSR); - - switch (type) { - case LIRC_IRDEO: - case LIRC_IRDEO_REMOTE: - /* setup port to 7N1 @ 115200 Baud */ - /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */ - - /* Set DLAB 1. */ - soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); - /* Set divisor to 1 => 115200 Baud */ - soutp(UART_DLM, 0); - soutp(UART_DLL, 1); - /* Set DLAB 0 + 7N1 */ - soutp(UART_LCR, UART_LCR_WLEN7); - /* THR interrupt already disabled at this point */ - break; - default: - break; - } - - return 0; -} - -static int init_port(void) -{ - int i, nlow, nhigh, result; - - result = request_irq(irq, irq_handler, - (share_irq ? IRQF_SHARED : 0), - LIRC_DRIVER_NAME, (void *)&hardware); - - switch (result) { - case -EBUSY: - printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq); - return -EBUSY; - case -EINVAL: - printk(KERN_ERR LIRC_DRIVER_NAME - ": Bad irq number or handler\n"); - return -EINVAL; - default: - break; - }; - - /* Reserve io region. */ - /* - * Future MMAP-Developers: Attention! - * For memory mapped I/O you *might* need to use ioremap() first, - * for the NSLU2 it's done in boot code. - */ - if (((iommap != 0) - && (request_mem_region(iommap, 8 << ioshift, - LIRC_DRIVER_NAME) == NULL)) - || ((iommap == 0) - && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": port %04x already in use\n", io); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": use 'setserial /dev/ttySX uart none'\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": or compile the serial port driver as module and\n"); - printk(KERN_WARNING LIRC_DRIVER_NAME - ": make sure this module is loaded first\n"); - return -EBUSY; - } - - if (hardware_init_port() < 0) - return -EINVAL; - - /* Initialize pulse/space widths */ - init_timing_params(duty_cycle, freq); - - /* If pin is high, then this must be an active low receiver. */ - if (sense == -1) { - /* wait 1/2 sec for the power supply */ - msleep(500); - - /* - * probe 9 times every 0.04s, collect "votes" for - * active high/low - */ - nlow = 0; - nhigh = 0; - for (i = 0; i < 9; i++) { - if (sinp(UART_MSR) & hardware[type].signal_pin) - nlow++; - else - nhigh++; - msleep(40); - } - sense = (nlow >= nhigh ? 1 : 0); - printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active " - "%s receiver\n", sense ? "low" : "high"); - } else - printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active " - "%s receiver\n", sense ? "low" : "high"); - - dprintk("Interrupt %d, port %04x obtained\n", irq, io); - return 0; -} - -static int set_use_inc(void *data) -{ - unsigned long flags; - - /* initialize timestamp */ - do_gettimeofday(&lasttv); - - spin_lock_irqsave(&hardware[type].lock, flags); - - /* Set DLAB 0. */ - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); - - soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); - - spin_unlock_irqrestore(&hardware[type].lock, flags); - - return 0; -} - -static void set_use_dec(void *data) -{ unsigned long flags; - - spin_lock_irqsave(&hardware[type].lock, flags); - - /* Set DLAB 0. */ - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); - - /* First of all, disable all interrupts */ - soutp(UART_IER, sinp(UART_IER) & - (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); - spin_unlock_irqrestore(&hardware[type].lock, flags); -} - -static ssize_t lirc_write(struct file *file, const char *buf, - size_t n, loff_t *ppos) -{ - int i, count; - unsigned long flags; - long delta = 0; - int *wbuf; - - if (!(hardware[type].features & LIRC_CAN_SEND_PULSE)) - return -EBADF; - - count = n / sizeof(int); - if (n % sizeof(int) || count % 2 == 0) - return -EINVAL; - wbuf = memdup_user(buf, n); - if (IS_ERR(wbuf)) - return PTR_ERR(wbuf); - spin_lock_irqsave(&hardware[type].lock, flags); - if (type == LIRC_IRDEO) { - /* DTR, RTS down */ - on(); - } - for (i = 0; i < count; i++) { - if (i%2) - hardware[type].send_space(wbuf[i] - delta); - else - delta = hardware[type].send_pulse(wbuf[i]); - } - off(); - spin_unlock_irqrestore(&hardware[type].lock, flags); - kfree(wbuf); - return n; -} - -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - int result; - __u32 value; - - switch (cmd) { - case LIRC_GET_SEND_MODE: - if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) - return -ENOIOCTLCMD; - - result = put_user(LIRC_SEND2MODE - (hardware[type].features&LIRC_CAN_SEND_MASK), - (__u32 *) arg); - if (result) - return result; - break; - - case LIRC_SET_SEND_MODE: - if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) - return -ENOIOCTLCMD; - - result = get_user(value, (__u32 *) arg); - if (result) - return result; - /* only LIRC_MODE_PULSE supported */ - if (value != LIRC_MODE_PULSE) - return -ENOSYS; - break; - - case LIRC_GET_LENGTH: - return -ENOSYS; - break; - - case LIRC_SET_SEND_DUTY_CYCLE: - dprintk("SET_SEND_DUTY_CYCLE\n"); - if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE)) - return -ENOIOCTLCMD; - - result = get_user(value, (__u32 *) arg); - if (result) - return result; - if (value <= 0 || value > 100) - return -EINVAL; - return init_timing_params(value, freq); - break; - - case LIRC_SET_SEND_CARRIER: - dprintk("SET_SEND_CARRIER\n"); - if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER)) - return -ENOIOCTLCMD; - - result = get_user(value, (__u32 *) arg); - if (result) - return result; - if (value > 500000 || value < 20000) - return -EINVAL; - return init_timing_params(duty_cycle, value); - break; - - default: - return lirc_dev_fop_ioctl(filep, cmd, arg); - } - return 0; -} - -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .write = lirc_write, - .unlocked_ioctl = lirc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lirc_ioctl, -#endif - .read = lirc_dev_fop_read, - .poll = lirc_dev_fop_poll, - .open = lirc_dev_fop_open, - .release = lirc_dev_fop_close, - .llseek = no_llseek, -}; - -static struct lirc_driver driver = { - .name = LIRC_DRIVER_NAME, - .minor = -1, - .code_length = 1, - .sample_rate = 0, - .data = NULL, - .add_to_buf = NULL, - .rbuf = &rbuf, - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .fops = &lirc_fops, - .dev = NULL, - .owner = THIS_MODULE, -}; - -static struct platform_device *lirc_serial_dev; - -static int __devinit lirc_serial_probe(struct platform_device *dev) -{ - return 0; -} - -static int __devexit lirc_serial_remove(struct platform_device *dev) -{ - return 0; -} - -static int lirc_serial_suspend(struct platform_device *dev, - pm_message_t state) -{ - /* Set DLAB 0. */ - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); - - /* Disable all interrupts */ - soutp(UART_IER, sinp(UART_IER) & - (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); - - /* Clear registers. */ - sinp(UART_LSR); - sinp(UART_RX); - sinp(UART_IIR); - sinp(UART_MSR); - - return 0; -} - -/* twisty maze... need a forward-declaration here... */ -static void lirc_serial_exit(void); - -static int lirc_serial_resume(struct platform_device *dev) -{ - unsigned long flags; - - if (hardware_init_port() < 0) { - lirc_serial_exit(); - return -EINVAL; - } - - spin_lock_irqsave(&hardware[type].lock, flags); - /* Enable Interrupt */ - do_gettimeofday(&lasttv); - soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); - off(); - - lirc_buffer_clear(&rbuf); - - spin_unlock_irqrestore(&hardware[type].lock, flags); - - return 0; -} - -static struct platform_driver lirc_serial_driver = { - .probe = lirc_serial_probe, - .remove = __devexit_p(lirc_serial_remove), - .suspend = lirc_serial_suspend, - .resume = lirc_serial_resume, - .driver = { - .name = "lirc_serial", - .owner = THIS_MODULE, - }, -}; - -static int __init lirc_serial_init(void) -{ - int result; - - /* Init read buffer. */ - result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); - if (result < 0) - return -ENOMEM; - - result = platform_driver_register(&lirc_serial_driver); - if (result) { - printk("lirc register returned %d\n", result); - goto exit_buffer_free; - } - - lirc_serial_dev = platform_device_alloc("lirc_serial", 0); - if (!lirc_serial_dev) { - result = -ENOMEM; - goto exit_driver_unregister; - } - - result = platform_device_add(lirc_serial_dev); - if (result) - goto exit_device_put; - - return 0; - -exit_device_put: - platform_device_put(lirc_serial_dev); -exit_driver_unregister: - platform_driver_unregister(&lirc_serial_driver); -exit_buffer_free: - lirc_buffer_free(&rbuf); - return result; -} - -static void lirc_serial_exit(void) -{ - platform_device_unregister(lirc_serial_dev); - platform_driver_unregister(&lirc_serial_driver); - lirc_buffer_free(&rbuf); -} - -static int __init lirc_serial_init_module(void) -{ - int result; - - result = lirc_serial_init(); - if (result) - return result; - - switch (type) { - case LIRC_HOMEBREW: - case LIRC_IRDEO: - case LIRC_IRDEO_REMOTE: - case LIRC_ANIMAX: - case LIRC_IGOR: - /* if nothing specified, use ttyS0/com1 and irq 4 */ - io = io ? io : 0x3f8; - irq = irq ? irq : 4; - break; -#ifdef CONFIG_LIRC_SERIAL_NSLU2 - case LIRC_NSLU2: - io = io ? io : IRQ_IXP4XX_UART2; - irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET); - iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS; - ioshift = ioshift ? ioshift : 2; - break; -#endif - default: - result = -EINVAL; - goto exit_serial_exit; - } - if (!softcarrier) { - switch (type) { - case LIRC_HOMEBREW: - case LIRC_IGOR: -#ifdef CONFIG_LIRC_SERIAL_NSLU2 - case LIRC_NSLU2: -#endif - hardware[type].features &= - ~(LIRC_CAN_SET_SEND_DUTY_CYCLE| - LIRC_CAN_SET_SEND_CARRIER); - break; - } - } - - result = init_port(); - if (result < 0) - goto exit_serial_exit; - driver.features = hardware[type].features; - driver.dev = &lirc_serial_dev->dev; - driver.minor = lirc_register_driver(&driver); - if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": register_chrdev failed!\n"); - result = -EIO; - goto exit_release; - } - return 0; -exit_release: - release_region(io, 8); -exit_serial_exit: - lirc_serial_exit(); - return result; -} - -static void __exit lirc_serial_exit_module(void) -{ - lirc_serial_exit(); - - free_irq(irq, (void *)&hardware); - - if (iommap != 0) - release_mem_region(iommap, 8 << ioshift); - else - release_region(io, 8); - lirc_unregister_driver(driver.minor); - dprintk("cleaned up module\n"); -} - - -module_init(lirc_serial_init_module); -module_exit(lirc_serial_exit_module); - -MODULE_DESCRIPTION("Infra-red receiver driver for serial ports."); -MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, " - "Christoph Bartelmus, Andrei Tanas"); -MODULE_LICENSE("GPL"); - -module_param(type, int, S_IRUGO); -MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo," - " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug," - " 5 = NSLU2 RX:CTS2/TX:GreenLED)"); - -module_param(io, int, S_IRUGO); -MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); - -/* some architectures (e.g. intel xscale) have memory mapped registers */ -module_param(iommap, bool, S_IRUGO); -MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O" - " (0 = no memory mapped io)"); - -/* - * some architectures (e.g. intel xscale) align the 8bit serial registers - * on 32bit word boundaries. - * See linux-kernel/serial/8250.c serial_in()/out() - */ -module_param(ioshift, int, S_IRUGO); -MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)"); - -module_param(irq, int, S_IRUGO); -MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); - -module_param(share_irq, bool, S_IRUGO); -MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)"); - -module_param(sense, bool, S_IRUGO); -MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" - " (0 = active high, 1 = active low )"); - -#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER -module_param(txsense, bool, S_IRUGO); -MODULE_PARM_DESC(txsense, "Sense of transmitter circuit" - " (0 = active high, 1 = active low )"); -#endif - -module_param(softcarrier, bool, S_IRUGO); -MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/lirc/lirc_sir.c b/drivers/staging/lirc/lirc_sir.c deleted file mode 100644 index 6903d3992eca..000000000000 --- a/drivers/staging/lirc/lirc_sir.c +++ /dev/null @@ -1,1279 +0,0 @@ -/* - * LIRC SIR driver, (C) 2000 Milan Pikula - * - * lirc_sir - Device driver for use with SIR (serial infra red) - * mode of IrDA on many notebooks. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - * - * 2000/09/16 Frank Przybylski : - * added timeout and relaxed pulse detection, removed gap bug - * - * 2000/12/15 Christoph Bartelmus : - * added support for Tekram Irmate 210 (sending does not work yet, - * kind of disappointing that nobody was able to implement that - * before), - * major clean-up - * - * 2001/02/27 Christoph Bartelmus : - * added support for StrongARM SA1100 embedded microprocessor - * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#ifdef LIRC_ON_SA1100 -#include -#ifdef CONFIG_SA1100_COLLIE -#include -#include -#endif -#endif - -#include - -#include -#include - -/* SECTION: Definitions */ - -/*** Tekram dongle ***/ -#ifdef LIRC_SIR_TEKRAM -/* stolen from kernel source */ -/* definitions for Tekram dongle */ -#define TEKRAM_115200 0x00 -#define TEKRAM_57600 0x01 -#define TEKRAM_38400 0x02 -#define TEKRAM_19200 0x03 -#define TEKRAM_9600 0x04 -#define TEKRAM_2400 0x08 - -#define TEKRAM_PW 0x10 /* Pulse select bit */ - -/* 10bit * 1s/115200bit in milliseconds = 87ms*/ -#define TIME_CONST (10000000ul/115200ul) - -#endif - -#ifdef LIRC_SIR_ACTISYS_ACT200L -static void init_act200(void); -#elif defined(LIRC_SIR_ACTISYS_ACT220L) -static void init_act220(void); -#endif - -/*** SA1100 ***/ -#ifdef LIRC_ON_SA1100 -struct sa1100_ser2_registers { - /* HSSP control register */ - unsigned char hscr0; - /* UART registers */ - unsigned char utcr0; - unsigned char utcr1; - unsigned char utcr2; - unsigned char utcr3; - unsigned char utcr4; - unsigned char utdr; - unsigned char utsr0; - unsigned char utsr1; -} sr; - -static int irq = IRQ_Ser2ICP; - -#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0 - -/* pulse/space ratio of 50/50 */ -static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); -/* 1000000/freq-pulse_width */ -static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); -static unsigned int freq = 38000; /* modulation frequency */ -static unsigned int duty_cycle = 50; /* duty cycle of 50% */ - -#endif - -#define RBUF_LEN 1024 -#define WBUF_LEN 1024 - -#define LIRC_DRIVER_NAME "lirc_sir" - -#define PULSE '[' - -#ifndef LIRC_SIR_TEKRAM -/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/ -#define TIME_CONST (9000000ul/115200ul) -#endif - - -/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */ -#define SIR_TIMEOUT (HZ*5/100) - -#ifndef LIRC_ON_SA1100 -#ifndef LIRC_IRQ -#define LIRC_IRQ 4 -#endif -#ifndef LIRC_PORT -/* for external dongles, default to com1 */ -#if defined(LIRC_SIR_ACTISYS_ACT200L) || \ - defined(LIRC_SIR_ACTISYS_ACT220L) || \ - defined(LIRC_SIR_TEKRAM) -#define LIRC_PORT 0x3f8 -#else -/* onboard sir ports are typically com3 */ -#define LIRC_PORT 0x3e8 -#endif -#endif - -static int io = LIRC_PORT; -static int irq = LIRC_IRQ; -static int threshold = 3; -#endif - -static DEFINE_SPINLOCK(timer_lock); -static struct timer_list timerlist; -/* time of last signal change detected */ -static struct timeval last_tv = {0, 0}; -/* time of last UART data ready interrupt */ -static struct timeval last_intr_tv = {0, 0}; -static int last_value; - -static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue); - -static DEFINE_SPINLOCK(hardware_lock); - -static int rx_buf[RBUF_LEN]; -static unsigned int rx_tail, rx_head; - -static int debug; -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ - fmt, ## args); \ - } while (0) - -/* SECTION: Prototypes */ - -/* Communication with user-space */ -static unsigned int lirc_poll(struct file *file, poll_table *wait); -static ssize_t lirc_read(struct file *file, char *buf, size_t count, - loff_t *ppos); -static ssize_t lirc_write(struct file *file, const char *buf, size_t n, - loff_t *pos); -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); -static void add_read_queue(int flag, unsigned long val); -static int init_chrdev(void); -static void drop_chrdev(void); -/* Hardware */ -static irqreturn_t sir_interrupt(int irq, void *dev_id); -static void send_space(unsigned long len); -static void send_pulse(unsigned long len); -static int init_hardware(void); -static void drop_hardware(void); -/* Initialisation */ -static int init_port(void); -static void drop_port(void); - -#ifdef LIRC_ON_SA1100 -static void on(void) -{ - PPSR |= PPC_TXD2; -} - -static void off(void) -{ - PPSR &= ~PPC_TXD2; -} -#else -static inline unsigned int sinp(int offset) -{ - return inb(io + offset); -} - -static inline void soutp(int offset, int value) -{ - outb(value, io + offset); -} -#endif - -#ifndef MAX_UDELAY_MS -#define MAX_UDELAY_US 5000 -#else -#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) -#endif - -static void safe_udelay(unsigned long usecs) -{ - while (usecs > MAX_UDELAY_US) { - udelay(MAX_UDELAY_US); - usecs -= MAX_UDELAY_US; - } - udelay(usecs); -} - -/* SECTION: Communication with user-space */ - -static unsigned int lirc_poll(struct file *file, poll_table *wait) -{ - poll_wait(file, &lirc_read_queue, wait); - if (rx_head != rx_tail) - return POLLIN | POLLRDNORM; - return 0; -} - -static ssize_t lirc_read(struct file *file, char *buf, size_t count, - loff_t *ppos) -{ - int n = 0; - int retval = 0; - DECLARE_WAITQUEUE(wait, current); - - if (count % sizeof(int)) - return -EINVAL; - - add_wait_queue(&lirc_read_queue, &wait); - set_current_state(TASK_INTERRUPTIBLE); - while (n < count) { - if (rx_head != rx_tail) { - if (copy_to_user((void *) buf + n, - (void *) (rx_buf + rx_head), - sizeof(int))) { - retval = -EFAULT; - break; - } - rx_head = (rx_head + 1) & (RBUF_LEN - 1); - n += sizeof(int); - } else { - if (file->f_flags & O_NONBLOCK) { - retval = -EAGAIN; - break; - } - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } - } - remove_wait_queue(&lirc_read_queue, &wait); - set_current_state(TASK_RUNNING); - return n ? n : retval; -} -static ssize_t lirc_write(struct file *file, const char *buf, size_t n, - loff_t *pos) -{ - unsigned long flags; - int i, count; - int *tx_buf; - - count = n / sizeof(int); - if (n % sizeof(int) || count % 2 == 0) - return -EINVAL; - tx_buf = memdup_user(buf, n); - if (IS_ERR(tx_buf)) - return PTR_ERR(tx_buf); - i = 0; -#ifdef LIRC_ON_SA1100 - /* disable receiver */ - Ser2UTCR3 = 0; -#endif - local_irq_save(flags); - while (1) { - if (i >= count) - break; - if (tx_buf[i]) - send_pulse(tx_buf[i]); - i++; - if (i >= count) - break; - if (tx_buf[i]) - send_space(tx_buf[i]); - i++; - } - local_irq_restore(flags); -#ifdef LIRC_ON_SA1100 - off(); - udelay(1000); /* wait 1ms for IR diode to recover */ - Ser2UTCR3 = 0; - /* clear status register to prevent unwanted interrupts */ - Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - /* enable receiver */ - Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; -#endif - kfree(tx_buf); - return count; -} - -static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - int retval = 0; - __u32 value = 0; -#ifdef LIRC_ON_SA1100 - - if (cmd == LIRC_GET_FEATURES) - value = LIRC_CAN_SEND_PULSE | - LIRC_CAN_SET_SEND_DUTY_CYCLE | - LIRC_CAN_SET_SEND_CARRIER | - LIRC_CAN_REC_MODE2; - else if (cmd == LIRC_GET_SEND_MODE) - value = LIRC_MODE_PULSE; - else if (cmd == LIRC_GET_REC_MODE) - value = LIRC_MODE_MODE2; -#else - if (cmd == LIRC_GET_FEATURES) - value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; - else if (cmd == LIRC_GET_SEND_MODE) - value = LIRC_MODE_PULSE; - else if (cmd == LIRC_GET_REC_MODE) - value = LIRC_MODE_MODE2; -#endif - - switch (cmd) { - case LIRC_GET_FEATURES: - case LIRC_GET_SEND_MODE: - case LIRC_GET_REC_MODE: - retval = put_user(value, (__u32 *) arg); - break; - - case LIRC_SET_SEND_MODE: - case LIRC_SET_REC_MODE: - retval = get_user(value, (__u32 *) arg); - break; -#ifdef LIRC_ON_SA1100 - case LIRC_SET_SEND_DUTY_CYCLE: - retval = get_user(value, (__u32 *) arg); - if (retval) - return retval; - if (value <= 0 || value > 100) - return -EINVAL; - /* (value/100)*(1000000/freq) */ - duty_cycle = value; - pulse_width = (unsigned long) duty_cycle*10000/freq; - space_width = (unsigned long) 1000000L/freq-pulse_width; - if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - break; - case LIRC_SET_SEND_CARRIER: - retval = get_user(value, (__u32 *) arg); - if (retval) - return retval; - if (value > 500000 || value < 20000) - return -EINVAL; - freq = value; - pulse_width = (unsigned long) duty_cycle*10000/freq; - space_width = (unsigned long) 1000000L/freq-pulse_width; - if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) - space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; - break; -#endif - default: - retval = -ENOIOCTLCMD; - - } - - if (retval) - return retval; - if (cmd == LIRC_SET_REC_MODE) { - if (value != LIRC_MODE_MODE2) - retval = -ENOSYS; - } else if (cmd == LIRC_SET_SEND_MODE) { - if (value != LIRC_MODE_PULSE) - retval = -ENOSYS; - } - - return retval; -} - -static void add_read_queue(int flag, unsigned long val) -{ - unsigned int new_rx_tail; - int newval; - - dprintk("add flag %d with val %lu\n", flag, val); - - newval = val & PULSE_MASK; - - /* - * statistically, pulses are ~TIME_CONST/2 too long. we could - * maybe make this more exact, but this is good enough - */ - if (flag) { - /* pulse */ - if (newval > TIME_CONST/2) - newval -= TIME_CONST/2; - else /* should not ever happen */ - newval = 1; - newval |= PULSE_BIT; - } else { - newval += TIME_CONST/2; - } - new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); - if (new_rx_tail == rx_head) { - dprintk("Buffer overrun.\n"); - return; - } - rx_buf[rx_tail] = newval; - rx_tail = new_rx_tail; - wake_up_interruptible(&lirc_read_queue); -} - -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .read = lirc_read, - .write = lirc_write, - .poll = lirc_poll, - .unlocked_ioctl = lirc_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = lirc_ioctl, -#endif - .open = lirc_dev_fop_open, - .release = lirc_dev_fop_close, - .llseek = no_llseek, -}; - -static int set_use_inc(void *data) -{ - return 0; -} - -static void set_use_dec(void *data) -{ -} - -static struct lirc_driver driver = { - .name = LIRC_DRIVER_NAME, - .minor = -1, - .code_length = 1, - .sample_rate = 0, - .data = NULL, - .add_to_buf = NULL, - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .fops = &lirc_fops, - .dev = NULL, - .owner = THIS_MODULE, -}; - - -static int init_chrdev(void) -{ - driver.minor = lirc_register_driver(&driver); - if (driver.minor < 0) { - printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); - return -EIO; - } - return 0; -} - -static void drop_chrdev(void) -{ - lirc_unregister_driver(driver.minor); -} - -/* SECTION: Hardware */ -static long delta(struct timeval *tv1, struct timeval *tv2) -{ - unsigned long deltv; - - deltv = tv2->tv_sec - tv1->tv_sec; - if (deltv > 15) - deltv = 0xFFFFFF; - else - deltv = deltv*1000000 + - tv2->tv_usec - - tv1->tv_usec; - return deltv; -} - -static void sir_timeout(unsigned long data) -{ - /* - * if last received signal was a pulse, but receiving stopped - * within the 9 bit frame, we need to finish this pulse and - * simulate a signal change to from pulse to space. Otherwise - * upper layers will receive two sequences next time. - */ - - unsigned long flags; - unsigned long pulse_end; - - /* avoid interference with interrupt */ - spin_lock_irqsave(&timer_lock, flags); - if (last_value) { -#ifndef LIRC_ON_SA1100 - /* clear unread bits in UART and restart */ - outb(UART_FCR_CLEAR_RCVR, io + UART_FCR); -#endif - /* determine 'virtual' pulse end: */ - pulse_end = delta(&last_tv, &last_intr_tv); - dprintk("timeout add %d for %lu usec\n", last_value, pulse_end); - add_read_queue(last_value, pulse_end); - last_value = 0; - last_tv = last_intr_tv; - } - spin_unlock_irqrestore(&timer_lock, flags); -} - -static irqreturn_t sir_interrupt(int irq, void *dev_id) -{ - unsigned char data; - struct timeval curr_tv; - static unsigned long deltv; -#ifdef LIRC_ON_SA1100 - int status; - static int n; - - status = Ser2UTSR0; - /* - * Deal with any receive errors first. The bytes in error may be - * the only bytes in the receive FIFO, so we do this first. - */ - while (status & UTSR0_EIF) { - int bstat; - - if (debug) { - dprintk("EIF\n"); - bstat = Ser2UTSR1; - - if (bstat & UTSR1_FRE) - dprintk("frame error\n"); - if (bstat & UTSR1_ROR) - dprintk("receive fifo overrun\n"); - if (bstat & UTSR1_PRE) - dprintk("parity error\n"); - } - - bstat = Ser2UTDR; - n++; - status = Ser2UTSR0; - } - - if (status & (UTSR0_RFS | UTSR0_RID)) { - do_gettimeofday(&curr_tv); - deltv = delta(&last_tv, &curr_tv); - do { - data = Ser2UTDR; - dprintk("%d data: %u\n", n, (unsigned int) data); - n++; - } while (status & UTSR0_RID && /* do not empty fifo in order to - * get UTSR0_RID in any case */ - Ser2UTSR1 & UTSR1_RNE); /* data ready */ - - if (status&UTSR0_RID) { - add_read_queue(0 , deltv - n * TIME_CONST); /*space*/ - add_read_queue(1, n * TIME_CONST); /*pulse*/ - n = 0; - last_tv = curr_tv; - } - } - - if (status & UTSR0_TFS) - printk(KERN_ERR "transmit fifo not full, shouldn't happen\n"); - - /* We must clear certain bits. */ - status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - if (status) - Ser2UTSR0 = status; -#else - unsigned long deltintrtv; - unsigned long flags; - int iir, lsr; - - while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) { - switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */ - case UART_IIR_MSI: - (void) inb(io + UART_MSR); - break; - case UART_IIR_RLSI: - (void) inb(io + UART_LSR); - break; - case UART_IIR_THRI: -#if 0 - if (lsr & UART_LSR_THRE) /* FIFO is empty */ - outb(data, io + UART_TX) -#endif - break; - case UART_IIR_RDI: - /* avoid interference with timer */ - spin_lock_irqsave(&timer_lock, flags); - do { - del_timer(&timerlist); - data = inb(io + UART_RX); - do_gettimeofday(&curr_tv); - deltv = delta(&last_tv, &curr_tv); - deltintrtv = delta(&last_intr_tv, &curr_tv); - dprintk("t %lu, d %d\n", deltintrtv, (int)data); - /* - * if nothing came in last X cycles, - * it was gap - */ - if (deltintrtv > TIME_CONST * threshold) { - if (last_value) { - dprintk("GAP\n"); - /* simulate signal change */ - add_read_queue(last_value, - deltv - - deltintrtv); - last_value = 0; - last_tv.tv_sec = - last_intr_tv.tv_sec; - last_tv.tv_usec = - last_intr_tv.tv_usec; - deltv = deltintrtv; - } - } - data = 1; - if (data ^ last_value) { - /* - * deltintrtv > 2*TIME_CONST, remember? - * the other case is timeout - */ - add_read_queue(last_value, - deltv-TIME_CONST); - last_value = data; - last_tv = curr_tv; - if (last_tv.tv_usec >= TIME_CONST) { - last_tv.tv_usec -= TIME_CONST; - } else { - last_tv.tv_sec--; - last_tv.tv_usec += 1000000 - - TIME_CONST; - } - } - last_intr_tv = curr_tv; - if (data) { - /* - * start timer for end of - * sequence detection - */ - timerlist.expires = jiffies + - SIR_TIMEOUT; - add_timer(&timerlist); - } - - lsr = inb(io + UART_LSR); - } while (lsr & UART_LSR_DR); /* data ready */ - spin_unlock_irqrestore(&timer_lock, flags); - break; - default: - break; - } - } -#endif - return IRQ_RETVAL(IRQ_HANDLED); -} - -#ifdef LIRC_ON_SA1100 -static void send_pulse(unsigned long length) -{ - unsigned long k, delay; - int flag; - - if (length == 0) - return; - /* - * this won't give us the carrier frequency we really want - * due to integer arithmetic, but we can accept this inaccuracy - */ - - for (k = flag = 0; k < length; k += delay, flag = !flag) { - if (flag) { - off(); - delay = space_width; - } else { - on(); - delay = pulse_width; - } - safe_udelay(delay); - } - off(); -} - -static void send_space(unsigned long length) -{ - if (length == 0) - return; - off(); - safe_udelay(length); -} -#else -static void send_space(unsigned long len) -{ - safe_udelay(len); -} - -static void send_pulse(unsigned long len) -{ - long bytes_out = len / TIME_CONST; - - if (bytes_out == 0) - bytes_out++; - - while (bytes_out--) { - outb(PULSE, io + UART_TX); - /* FIXME treba seriozne cakanie z char/serial.c */ - while (!(inb(io + UART_LSR) & UART_LSR_THRE)) - ; - } -} -#endif - -#ifdef CONFIG_SA1100_COLLIE -static int sa1100_irda_set_power_collie(int state) -{ - if (state) { - /* - * 0 - off - * 1 - short range, lowest power - * 2 - medium range, medium power - * 3 - maximum range, high power - */ - ucb1200_set_io_direction(TC35143_GPIO_IR_ON, - TC35143_IODIR_OUTPUT); - ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW); - udelay(100); - } else { - /* OFF */ - ucb1200_set_io_direction(TC35143_GPIO_IR_ON, - TC35143_IODIR_OUTPUT); - ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH); - } - return 0; -} -#endif - -static int init_hardware(void) -{ - unsigned long flags; - - spin_lock_irqsave(&hardware_lock, flags); - /* reset UART */ -#ifdef LIRC_ON_SA1100 -#ifdef CONFIG_SA1100_BITSY - if (machine_is_bitsy()) { - printk(KERN_INFO "Power on IR module\n"); - set_bitsy_egpio(EGPIO_BITSY_IR_ON); - } -#endif -#ifdef CONFIG_SA1100_COLLIE - sa1100_irda_set_power_collie(3); /* power on */ -#endif - sr.hscr0 = Ser2HSCR0; - - sr.utcr0 = Ser2UTCR0; - sr.utcr1 = Ser2UTCR1; - sr.utcr2 = Ser2UTCR2; - sr.utcr3 = Ser2UTCR3; - sr.utcr4 = Ser2UTCR4; - - sr.utdr = Ser2UTDR; - sr.utsr0 = Ser2UTSR0; - sr.utsr1 = Ser2UTSR1; - - /* configure GPIO */ - /* output */ - PPDR |= PPC_TXD2; - PSDR |= PPC_TXD2; - /* set output to 0 */ - off(); - - /* Enable HP-SIR modulation, and ensure that the port is disabled. */ - Ser2UTCR3 = 0; - Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP); - - /* clear status register to prevent unwanted interrupts */ - Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - - /* 7N1 */ - Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData; - /* 115200 */ - Ser2UTCR1 = 0; - Ser2UTCR2 = 1; - /* use HPSIR, 1.6 usec pulses */ - Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us; - - /* enable receiver, receive fifo interrupt */ - Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; - - /* clear status register to prevent unwanted interrupts */ - Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); - -#elif defined(LIRC_SIR_TEKRAM) - /* disable FIFO */ - soutp(UART_FCR, - UART_FCR_CLEAR_RCVR| - UART_FCR_CLEAR_XMIT| - UART_FCR_TRIGGER_1); - - /* Set DLAB 0. */ - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); - - /* First of all, disable all interrupts */ - soutp(UART_IER, sinp(UART_IER) & - (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); - - /* Set DLAB 1. */ - soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); - - /* Set divisor to 12 => 9600 Baud */ - soutp(UART_DLM, 0); - soutp(UART_DLL, 12); - - /* Set DLAB 0. */ - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); - - /* power supply */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); - safe_udelay(50*1000); - - /* -DTR low -> reset PIC */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); - udelay(1*1000); - - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); - udelay(100); - - - /* -RTS low -> send control byte */ - soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); - udelay(7); - soutp(UART_TX, TEKRAM_115200|TEKRAM_PW); - - /* one byte takes ~1042 usec to transmit at 9600,8N1 */ - udelay(1500); - - /* back to normal operation */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); - udelay(50); - - udelay(1500); - - /* read previous control byte */ - printk(KERN_INFO LIRC_DRIVER_NAME - ": 0x%02x\n", sinp(UART_RX)); - - /* Set DLAB 1. */ - soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); - - /* Set divisor to 1 => 115200 Baud */ - soutp(UART_DLM, 0); - soutp(UART_DLL, 1); - - /* Set DLAB 0, 8 Bit */ - soutp(UART_LCR, UART_LCR_WLEN8); - /* enable interrupts */ - soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); -#else - outb(0, io + UART_MCR); - outb(0, io + UART_IER); - /* init UART */ - /* set DLAB, speed = 115200 */ - outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR); - outb(1, io + UART_DLL); outb(0, io + UART_DLM); - /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */ - outb(UART_LCR_WLEN7, io + UART_LCR); - /* FIFO operation */ - outb(UART_FCR_ENABLE_FIFO, io + UART_FCR); - /* interrupts */ - /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */ - outb(UART_IER_RDI, io + UART_IER); - /* turn on UART */ - outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR); -#ifdef LIRC_SIR_ACTISYS_ACT200L - init_act200(); -#elif defined(LIRC_SIR_ACTISYS_ACT220L) - init_act220(); -#endif -#endif - spin_unlock_irqrestore(&hardware_lock, flags); - return 0; -} - -static void drop_hardware(void) -{ - unsigned long flags; - - spin_lock_irqsave(&hardware_lock, flags); - -#ifdef LIRC_ON_SA1100 - Ser2UTCR3 = 0; - - Ser2UTCR0 = sr.utcr0; - Ser2UTCR1 = sr.utcr1; - Ser2UTCR2 = sr.utcr2; - Ser2UTCR4 = sr.utcr4; - Ser2UTCR3 = sr.utcr3; - - Ser2HSCR0 = sr.hscr0; -#ifdef CONFIG_SA1100_BITSY - if (machine_is_bitsy()) - clr_bitsy_egpio(EGPIO_BITSY_IR_ON); -#endif -#ifdef CONFIG_SA1100_COLLIE - sa1100_irda_set_power_collie(0); /* power off */ -#endif -#else - /* turn off interrupts */ - outb(0, io + UART_IER); -#endif - spin_unlock_irqrestore(&hardware_lock, flags); -} - -/* SECTION: Initialisation */ - -static int init_port(void) -{ - int retval; - - /* get I/O port access and IRQ line */ -#ifndef LIRC_ON_SA1100 - if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { - printk(KERN_ERR LIRC_DRIVER_NAME - ": i/o port 0x%.4x already in use.\n", io); - return -EBUSY; - } -#endif - retval = request_irq(irq, sir_interrupt, 0, - LIRC_DRIVER_NAME, NULL); - if (retval < 0) { -# ifndef LIRC_ON_SA1100 - release_region(io, 8); -# endif - printk(KERN_ERR LIRC_DRIVER_NAME - ": IRQ %d already in use.\n", - irq); - return retval; - } -#ifndef LIRC_ON_SA1100 - printk(KERN_INFO LIRC_DRIVER_NAME - ": I/O port 0x%.4x, IRQ %d.\n", - io, irq); -#endif - - init_timer(&timerlist); - timerlist.function = sir_timeout; - timerlist.data = 0xabadcafe; - - return 0; -} - -static void drop_port(void) -{ - free_irq(irq, NULL); - del_timer_sync(&timerlist); -#ifndef LIRC_ON_SA1100 - release_region(io, 8); -#endif -} - -#ifdef LIRC_SIR_ACTISYS_ACT200L -/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */ -/* some code borrowed from Linux IRDA driver */ - -/* Register 0: Control register #1 */ -#define ACT200L_REG0 0x00 -#define ACT200L_TXEN 0x01 /* Enable transmitter */ -#define ACT200L_RXEN 0x02 /* Enable receiver */ -#define ACT200L_ECHO 0x08 /* Echo control chars */ - -/* Register 1: Control register #2 */ -#define ACT200L_REG1 0x10 -#define ACT200L_LODB 0x01 /* Load new baud rate count value */ -#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ - -/* Register 3: Transmit mode register #2 */ -#define ACT200L_REG3 0x30 -#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ -#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ -#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */ - -/* Register 4: Output Power register */ -#define ACT200L_REG4 0x40 -#define ACT200L_OP0 0x01 /* Enable LED1C output */ -#define ACT200L_OP1 0x02 /* Enable LED2C output */ -#define ACT200L_BLKR 0x04 - -/* Register 5: Receive Mode register */ -#define ACT200L_REG5 0x50 -#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ - /*.. other various IRDA bit modes, and TV remote modes..*/ - -/* Register 6: Receive Sensitivity register #1 */ -#define ACT200L_REG6 0x60 -#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ -#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ - -/* Register 7: Receive Sensitivity register #2 */ -#define ACT200L_REG7 0x70 -#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ - -/* Register 8,9: Baud Rate Divider register #1,#2 */ -#define ACT200L_REG8 0x80 -#define ACT200L_REG9 0x90 - -#define ACT200L_2400 0x5f -#define ACT200L_9600 0x17 -#define ACT200L_19200 0x0b -#define ACT200L_38400 0x05 -#define ACT200L_57600 0x03 -#define ACT200L_115200 0x01 - -/* Register 13: Control register #3 */ -#define ACT200L_REG13 0xd0 -#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ - -/* Register 15: Status register */ -#define ACT200L_REG15 0xf0 - -/* Register 21: Control register #4 */ -#define ACT200L_REG21 0x50 -#define ACT200L_EXCK 0x02 /* Disable clock output driver */ -#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ - -static void init_act200(void) -{ - int i; - __u8 control[] = { - ACT200L_REG15, - ACT200L_REG13 | ACT200L_SHDW, - ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, - ACT200L_REG13, - ACT200L_REG7 | ACT200L_ENPOS, - ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, - ACT200L_REG5 | ACT200L_RWIDL, - ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, - ACT200L_REG3 | ACT200L_B0, - ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN, - ACT200L_REG8 | (ACT200L_115200 & 0x0f), - ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f), - ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE - }; - - /* Set DLAB 1. */ - soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); - - /* Set divisor to 12 => 9600 Baud */ - soutp(UART_DLM, 0); - soutp(UART_DLL, 12); - - /* Set DLAB 0. */ - soutp(UART_LCR, UART_LCR_WLEN8); - /* Set divisor to 12 => 9600 Baud */ - - /* power supply */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); - for (i = 0; i < 50; i++) - safe_udelay(1000); - - /* Reset the dongle : set RTS low for 25 ms */ - soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); - for (i = 0; i < 25; i++) - udelay(1000); - - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); - udelay(100); - - /* Clear DTR and set RTS to enter command mode */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); - udelay(7); - - /* send out the control register settings for 115K 7N1 SIR operation */ - for (i = 0; i < sizeof(control); i++) { - soutp(UART_TX, control[i]); - /* one byte takes ~1042 usec to transmit at 9600,8N1 */ - udelay(1500); - } - - /* back to normal operation */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); - udelay(50); - - udelay(1500); - soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); - - /* Set DLAB 1. */ - soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7); - - /* Set divisor to 1 => 115200 Baud */ - soutp(UART_DLM, 0); - soutp(UART_DLL, 1); - - /* Set DLAB 0. */ - soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); - - /* Set DLAB 0, 7 Bit */ - soutp(UART_LCR, UART_LCR_WLEN7); - - /* enable interrupts */ - soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); -} -#endif - -#ifdef LIRC_SIR_ACTISYS_ACT220L -/* - * Derived from linux IrDA driver (net/irda/actisys.c) - * Drop me a mail for any kind of comment: maxx@spaceboyz.net - */ - -void init_act220(void) -{ - int i; - - /* DLAB 1 */ - soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7); - - /* 9600 baud */ - soutp(UART_DLM, 0); - soutp(UART_DLL, 12); - - /* DLAB 0 */ - soutp(UART_LCR, UART_LCR_WLEN7); - - /* reset the dongle, set DTR low for 10us */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); - udelay(10); - - /* back to normal (still 9600) */ - soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2); - - /* - * send RTS pulses until we reach 115200 - * i hope this is really the same for act220l/act220l+ - */ - for (i = 0; i < 3; i++) { - udelay(10); - /* set RTS low for 10 us */ - soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); - udelay(10); - /* set RTS high for 10 us */ - soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); - } - - /* back to normal operation */ - udelay(1500); /* better safe than sorry ;) */ - - /* Set DLAB 1. */ - soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7); - - /* Set divisor to 1 => 115200 Baud */ - soutp(UART_DLM, 0); - soutp(UART_DLL, 1); - - /* Set DLAB 0, 7 Bit */ - /* The dongle doesn't seem to have any problems with operation at 7N1 */ - soutp(UART_LCR, UART_LCR_WLEN7); - - /* enable interrupts */ - soutp(UART_IER, UART_IER_RDI); -} -#endif - -static int init_lirc_sir(void) -{ - int retval; - - init_waitqueue_head(&lirc_read_queue); - retval = init_port(); - if (retval < 0) - return retval; - init_hardware(); - printk(KERN_INFO LIRC_DRIVER_NAME - ": Installed.\n"); - return 0; -} - - -static int __init lirc_sir_init(void) -{ - int retval; - - retval = init_chrdev(); - if (retval < 0) - return retval; - retval = init_lirc_sir(); - if (retval) { - drop_chrdev(); - return retval; - } - return 0; -} - -static void __exit lirc_sir_exit(void) -{ - drop_hardware(); - drop_chrdev(); - drop_port(); - printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); -} - -module_init(lirc_sir_init); -module_exit(lirc_sir_exit); - -#ifdef LIRC_SIR_TEKRAM -MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210"); -MODULE_AUTHOR("Christoph Bartelmus"); -#elif defined(LIRC_ON_SA1100) -MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor"); -MODULE_AUTHOR("Christoph Bartelmus"); -#elif defined(LIRC_SIR_ACTISYS_ACT200L) -MODULE_DESCRIPTION("LIRC driver for Actisys Act200L"); -MODULE_AUTHOR("Karl Bongers"); -#elif defined(LIRC_SIR_ACTISYS_ACT220L) -MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)"); -MODULE_AUTHOR("Jan Roemisch"); -#else -MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports"); -MODULE_AUTHOR("Milan Pikula"); -#endif -MODULE_LICENSE("GPL"); - -#ifdef LIRC_ON_SA1100 -module_param(irq, int, S_IRUGO); -MODULE_PARM_DESC(irq, "Interrupt (16)"); -#else -module_param(io, int, S_IRUGO); -MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); - -module_param(irq, int, S_IRUGO); -MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); - -module_param(threshold, int, S_IRUGO); -MODULE_PARM_DESC(threshold, "space detection threshold (3)"); -#endif - -module_param(debug, bool, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/lirc/lirc_ttusbir.c b/drivers/staging/lirc/lirc_ttusbir.c deleted file mode 100644 index e4b329b8cafd..000000000000 --- a/drivers/staging/lirc/lirc_ttusbir.c +++ /dev/null @@ -1,395 +0,0 @@ -/* - * lirc_ttusbir.c - * - * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver - * - * Copyright (C) 2007 Stefan Macher - * - * This LIRC driver provides access to the TechnoTrend USB IR Receiver. - * The receiver delivers the IR signal as raw sampled true/false data in - * isochronous USB packets each of size 128 byte. - * Currently the driver reduces the sampling rate by factor of 8 as this - * is still more than enough to decode RC-5 - others should be analyzed. - * But the driver does not rely on RC-5 it should be able to decode every - * IR signal that is not too fast. - */ - -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 - -MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC"); -MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)"); -MODULE_LICENSE("GPL"); - -/* #define DEBUG */ -#ifdef DEBUG -#define DPRINTK printk -#else -#define DPRINTK(_x_, a...) -#endif - -/* function declarations */ -static int probe(struct usb_interface *intf, const struct usb_device_id *id); -static void disconnect(struct usb_interface *intf); -static void urb_complete(struct urb *urb); -static int set_use_inc(void *data); -static void set_use_dec(void *data); - -static int num_urbs = 2; -module_param(num_urbs, int, S_IRUGO); -MODULE_PARM_DESC(num_urbs, - "Number of URBs in queue. Try to increase to 4 in case " - "of problems (default: 2; minimum: 2)"); - -/* table of devices that work with this driver */ -static struct usb_device_id device_id_table[] = { - /* TechnoTrend USB IR Receiver */ - { USB_DEVICE(0x0B48, 0x2003) }, - /* Terminating entry */ - { } -}; -MODULE_DEVICE_TABLE(usb, device_id_table); - -/* USB driver definition */ -static struct usb_driver usb_driver = { - .name = "TTUSBIR", - .id_table = &(device_id_table[0]), - .probe = probe, - .disconnect = disconnect, -}; - -/* USB device definition */ -struct ttusbir_device { - struct usb_driver *usb_driver; - struct usb_device *udev; - struct usb_interface *interf; - struct usb_class_driver class_driver; - unsigned int ifnum; /* Interface number to use */ - unsigned int alt_setting; /* alternate setting to use */ - unsigned int endpoint; /* Endpoint to use */ - struct urb **urb; /* num_urb URB pointers*/ - char **buffer; /* 128 byte buffer for each URB */ - struct lirc_buffer rbuf; /* Buffer towards LIRC */ - struct lirc_driver driver; - int minor; - int last_pulse; /* remembers if last received byte was pulse or space */ - int last_num; /* remembers how many last bytes appeared */ - int opened; -}; - -/*** LIRC specific functions ***/ -static int set_use_inc(void *data) -{ - int i, retval; - struct ttusbir_device *ttusbir = data; - - DPRINTK("Sending first URBs\n"); - /* @TODO Do I need to check if I am already opened */ - ttusbir->opened = 1; - - for (i = 0; i < num_urbs; i++) { - retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL); - if (retval) { - err("%s: usb_submit_urb failed on urb %d", - __func__, i); - return retval; - } - } - return 0; -} - -static void set_use_dec(void *data) -{ - struct ttusbir_device *ttusbir = data; - - DPRINTK("Device closed\n"); - - ttusbir->opened = 0; -} - -/*** USB specific functions ***/ - -/* - * This mapping table is used to do a very simple filtering of the - * input signal. - * For a value with at least 4 bits set it returns 0xFF otherwise - * 0x00. For faster IR signals this can not be used. But for RC-5 we - * still have about 14 samples per pulse/space, i.e. we sample with 14 - * times higher frequency than the signal frequency - */ -const unsigned char map_table[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF -}; - -static void urb_complete(struct urb *urb) -{ - struct ttusbir_device *ttusbir; - unsigned char *buf; - int i; - int l; - - ttusbir = urb->context; - - if (!ttusbir->opened) - return; - - buf = (unsigned char *)urb->transfer_buffer; - - for (i = 0; i < 128; i++) { - /* Here we do the filtering and some kind of down sampling */ - buf[i] = ~map_table[buf[i]]; - if (ttusbir->last_pulse == buf[i]) { - if (ttusbir->last_num < PULSE_MASK/63) - ttusbir->last_num++; - /* - * else we are in a idle period and do not need to - * increment any longer - */ - } else { - l = ttusbir->last_num * 62; /* about 62 = us/byte */ - if (ttusbir->last_pulse) /* pulse or space? */ - l |= PULSE_BIT; - if (!lirc_buffer_full(&ttusbir->rbuf)) { - lirc_buffer_write(&ttusbir->rbuf, (void *)&l); - wake_up_interruptible(&ttusbir->rbuf.wait_poll); - } - ttusbir->last_num = 0; - ttusbir->last_pulse = buf[i]; - } - } - usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */ -} - -/* - * Called whenever the USB subsystem thinks we could be the right driver - * to handle this device - */ -static int probe(struct usb_interface *intf, const struct usb_device_id *id) -{ - int alt_set, endp; - int found = 0; - int i, j; - int struct_size; - struct usb_host_interface *host_interf; - struct usb_interface_descriptor *interf_desc; - struct usb_host_endpoint *host_endpoint; - struct ttusbir_device *ttusbir; - - DPRINTK("Module ttusbir probe\n"); - - /* To reduce memory fragmentation we use only one allocation */ - struct_size = sizeof(struct ttusbir_device) + - (sizeof(struct urb *) * num_urbs) + - (sizeof(char *) * num_urbs) + - (num_urbs * 128); - ttusbir = kzalloc(struct_size, GFP_KERNEL); - if (!ttusbir) - return -ENOMEM; - - ttusbir->urb = (struct urb **)((char *)ttusbir + - sizeof(struct ttusbir_device)); - ttusbir->buffer = (char **)((char *)ttusbir->urb + - (sizeof(struct urb *) * num_urbs)); - for (i = 0; i < num_urbs; i++) - ttusbir->buffer[i] = (char *)ttusbir->buffer + - (sizeof(char *)*num_urbs) + (i * 128); - - ttusbir->usb_driver = &usb_driver; - ttusbir->alt_setting = -1; - /* @TODO check if error can be returned */ - ttusbir->udev = usb_get_dev(interface_to_usbdev(intf)); - ttusbir->interf = intf; - ttusbir->last_pulse = 0x00; - ttusbir->last_num = 0; - - /* - * Now look for interface setting we can handle - * We are searching for the alt setting where end point - * 0x82 has max packet size 16 - */ - for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) { - host_interf = &intf->altsetting[alt_set]; - interf_desc = &host_interf->desc; - for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) { - host_endpoint = &host_interf->endpoint[endp]; - if ((host_endpoint->desc.bEndpointAddress == 0x82) && - (host_endpoint->desc.wMaxPacketSize == 0x10)) { - ttusbir->alt_setting = alt_set; - ttusbir->endpoint = endp; - found = 1; - break; - } - } - } - if (ttusbir->alt_setting != -1) - DPRINTK("alt setting: %d\n", ttusbir->alt_setting); - else { - err("Could not find alternate setting\n"); - kfree(ttusbir); - return -EINVAL; - } - - /* OK lets setup this interface setting */ - usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting); - - /* Store device info in interface structure */ - usb_set_intfdata(intf, ttusbir); - - /* Register as a LIRC driver */ - if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) { - err("Could not get memory for LIRC data buffer\n"); - usb_set_intfdata(intf, NULL); - kfree(ttusbir); - return -ENOMEM; - } - strcpy(ttusbir->driver.name, "TTUSBIR"); - ttusbir->driver.minor = -1; - ttusbir->driver.code_length = 1; - ttusbir->driver.sample_rate = 0; - ttusbir->driver.data = ttusbir; - ttusbir->driver.add_to_buf = NULL; - ttusbir->driver.rbuf = &ttusbir->rbuf; - ttusbir->driver.set_use_inc = set_use_inc; - ttusbir->driver.set_use_dec = set_use_dec; - ttusbir->driver.dev = &intf->dev; - ttusbir->driver.owner = THIS_MODULE; - ttusbir->driver.features = LIRC_CAN_REC_MODE2; - ttusbir->minor = lirc_register_driver(&ttusbir->driver); - if (ttusbir->minor < 0) { - err("Error registering as LIRC driver\n"); - usb_set_intfdata(intf, NULL); - lirc_buffer_free(&ttusbir->rbuf); - kfree(ttusbir); - return -EIO; - } - - /* Allocate and setup the URB that we will use to talk to the device */ - for (i = 0; i < num_urbs; i++) { - ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL); - if (!ttusbir->urb[i]) { - err("Could not allocate memory for the URB\n"); - for (j = i - 1; j >= 0; j--) - kfree(ttusbir->urb[j]); - lirc_buffer_free(&ttusbir->rbuf); - lirc_unregister_driver(ttusbir->minor); - kfree(ttusbir); - usb_set_intfdata(intf, NULL); - return -ENOMEM; - } - ttusbir->urb[i]->dev = ttusbir->udev; - ttusbir->urb[i]->context = ttusbir; - ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev, - ttusbir->endpoint); - ttusbir->urb[i]->interval = 1; - ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP; - ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0]; - ttusbir->urb[i]->complete = urb_complete; - ttusbir->urb[i]->number_of_packets = 8; - ttusbir->urb[i]->transfer_buffer_length = 128; - for (j = 0; j < 8; j++) { - ttusbir->urb[i]->iso_frame_desc[j].offset = j*16; - ttusbir->urb[i]->iso_frame_desc[j].length = 16; - } - } - return 0; -} - -/** - * Called when the driver is unloaded or the device is unplugged - */ -static void disconnect(struct usb_interface *intf) -{ - int i; - struct ttusbir_device *ttusbir; - - DPRINTK("Module ttusbir disconnect\n"); - - ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf); - usb_set_intfdata(intf, NULL); - lirc_unregister_driver(ttusbir->minor); - DPRINTK("unregistered\n"); - - for (i = 0; i < num_urbs; i++) { - usb_kill_urb(ttusbir->urb[i]); - usb_free_urb(ttusbir->urb[i]); - } - DPRINTK("URBs killed\n"); - lirc_buffer_free(&ttusbir->rbuf); - kfree(ttusbir); -} - -static int ttusbir_init_module(void) -{ - int result; - - DPRINTK(KERN_DEBUG "Module ttusbir init\n"); - - /* register this driver with the USB subsystem */ - result = usb_register(&usb_driver); - if (result) - err("usb_register failed. Error number %d", result); - return result; -} - -static void ttusbir_exit_module(void) -{ - printk(KERN_DEBUG "Module ttusbir exit\n"); - usb_deregister(&usb_driver); -} - -module_init(ttusbir_init_module); -module_exit(ttusbir_exit_module); diff --git a/drivers/staging/lirc/lirc_zilog.c b/drivers/staging/lirc/lirc_zilog.c deleted file mode 100644 index 0302d82a12f7..000000000000 --- a/drivers/staging/lirc/lirc_zilog.c +++ /dev/null @@ -1,1676 +0,0 @@ -/* - * i2c IR lirc driver for devices with zilog IR processors - * - * Copyright (c) 2000 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 Asus TV-Box and Creative/VisionTek BreakOut-Box by - * Stefan Jahn - * modified for inclusion into kernel sources by - * Jerome Brock - * modified for Leadtek Winfast PVR2000 by - * Thomas Reitmayr (treitmayr@yahoo.com) - * modified for Hauppauge PVR-150 IR TX device by - * Mark Weaver - * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150 - * Jarod Wilson - * - * parts are cut&pasted from the lirc_i2c.c driver - * - * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are - * Copyright (C) 2011 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -struct IR; - -struct IR_rx { - struct kref ref; - struct IR *ir; - - /* RX device */ - struct mutex client_lock; - struct i2c_client *c; - - /* RX polling thread data */ - struct task_struct *task; - - /* RX read data */ - unsigned char b[3]; - bool hdpvr_data_fmt; -}; - -struct IR_tx { - struct kref ref; - struct IR *ir; - - /* TX device */ - struct mutex client_lock; - struct i2c_client *c; - - /* TX additional actions needed */ - int need_boot; - bool post_tx_ready_poll; -}; - -struct IR { - struct kref ref; - struct list_head list; - - /* FIXME spinlock access to l.features */ - struct lirc_driver l; - struct lirc_buffer rbuf; - - struct mutex ir_lock; - atomic_t open_count; - - struct i2c_adapter *adapter; - - spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */ - struct IR_rx *rx; - - spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */ - struct IR_tx *tx; -}; - -/* IR transceiver instance object list */ -/* - * This lock is used for the following: - * a. ir_devices_list access, insertions, deletions - * b. struct IR kref get()s and put()s - * c. serialization of ir_probe() for the two i2c_clients for a Z8 - */ -static DEFINE_MUTEX(ir_devices_lock); -static LIST_HEAD(ir_devices_list); - -/* Block size for IR transmitter */ -#define TX_BLOCK_SIZE 99 - -/* Hauppauge IR transmitter data */ -struct tx_data_struct { - /* Boot block */ - unsigned char *boot_data; - - /* Start of binary data block */ - unsigned char *datap; - - /* End of binary data block */ - unsigned char *endp; - - /* Number of installed codesets */ - unsigned int num_code_sets; - - /* Pointers to codesets */ - unsigned char **code_sets; - - /* Global fixed data template */ - int fixed[TX_BLOCK_SIZE]; -}; - -static struct tx_data_struct *tx_data; -static struct mutex tx_data_lock; - -#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \ - ## args) -#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) -#define zilog_info(s, args...) printk(KERN_INFO KBUILD_MODNAME ": " s, ## args) - -/* module parameters */ -static int debug; /* debug output */ -static int tx_only; /* only handle the IR Tx function */ -static int minor = -1; /* minor number */ - -#define dprintk(fmt, args...) \ - do { \ - if (debug) \ - printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \ - ## args); \ - } while (0) - - -/* struct IR reference counting */ -static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held) -{ - if (ir_devices_lock_held) { - kref_get(&ir->ref); - } else { - mutex_lock(&ir_devices_lock); - kref_get(&ir->ref); - mutex_unlock(&ir_devices_lock); - } - return ir; -} - -static void release_ir_device(struct kref *ref) -{ - struct IR *ir = container_of(ref, struct IR, ref); - - /* - * Things should be in this state by now: - * ir->rx set to NULL and deallocated - happens before ir->rx->ir put() - * ir->rx->task kthread stopped - happens before ir->rx->ir put() - * ir->tx set to NULL and deallocated - happens before ir->tx->ir put() - * ir->open_count == 0 - happens on final close() - * ir_lock, tx_ref_lock, rx_ref_lock, all released - */ - if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { - lirc_unregister_driver(ir->l.minor); - ir->l.minor = MAX_IRCTL_DEVICES; - } - if (ir->rbuf.fifo_initialized) - lirc_buffer_free(&ir->rbuf); - list_del(&ir->list); - kfree(ir); -} - -static int put_ir_device(struct IR *ir, bool ir_devices_lock_held) -{ - int released; - - if (ir_devices_lock_held) - return kref_put(&ir->ref, release_ir_device); - - mutex_lock(&ir_devices_lock); - released = kref_put(&ir->ref, release_ir_device); - mutex_unlock(&ir_devices_lock); - - return released; -} - -/* struct IR_rx reference counting */ -static struct IR_rx *get_ir_rx(struct IR *ir) -{ - struct IR_rx *rx; - - spin_lock(&ir->rx_ref_lock); - rx = ir->rx; - if (rx != NULL) - kref_get(&rx->ref); - spin_unlock(&ir->rx_ref_lock); - return rx; -} - -static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held) -{ - /* end up polling thread */ - if (!IS_ERR_OR_NULL(rx->task)) { - kthread_stop(rx->task); - rx->task = NULL; - /* Put the ir ptr that ir_probe() gave to the rx poll thread */ - put_ir_device(rx->ir, ir_devices_lock_held); - } -} - -static void release_ir_rx(struct kref *ref) -{ - struct IR_rx *rx = container_of(ref, struct IR_rx, ref); - struct IR *ir = rx->ir; - - /* - * This release function can't do all the work, as we want - * to keep the rx_ref_lock a spinlock, and killing the poll thread - * and releasing the ir reference can cause a sleep. That work is - * performed by put_ir_rx() - */ - ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; - /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */ - ir->rx = NULL; - /* Don't do the kfree(rx) here; we still need to kill the poll thread */ - return; -} - -static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held) -{ - int released; - struct IR *ir = rx->ir; - - spin_lock(&ir->rx_ref_lock); - released = kref_put(&rx->ref, release_ir_rx); - spin_unlock(&ir->rx_ref_lock); - /* Destroy the rx kthread while not holding the spinlock */ - if (released) { - destroy_rx_kthread(rx, ir_devices_lock_held); - kfree(rx); - /* Make sure we're not still in a poll_table somewhere */ - wake_up_interruptible(&ir->rbuf.wait_poll); - } - /* Do a reference put() for the rx->ir reference, if we released rx */ - if (released) - put_ir_device(ir, ir_devices_lock_held); - return released; -} - -/* struct IR_tx reference counting */ -static struct IR_tx *get_ir_tx(struct IR *ir) -{ - struct IR_tx *tx; - - spin_lock(&ir->tx_ref_lock); - tx = ir->tx; - if (tx != NULL) - kref_get(&tx->ref); - spin_unlock(&ir->tx_ref_lock); - return tx; -} - -static void release_ir_tx(struct kref *ref) -{ - struct IR_tx *tx = container_of(ref, struct IR_tx, ref); - struct IR *ir = tx->ir; - - ir->l.features &= ~LIRC_CAN_SEND_PULSE; - /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */ - ir->tx = NULL; - kfree(tx); -} - -static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held) -{ - int released; - struct IR *ir = tx->ir; - - spin_lock(&ir->tx_ref_lock); - released = kref_put(&tx->ref, release_ir_tx); - spin_unlock(&ir->tx_ref_lock); - /* Do a reference put() for the tx->ir reference, if we released tx */ - if (released) - put_ir_device(ir, ir_devices_lock_held); - return released; -} - -static int add_to_buf(struct IR *ir) -{ - __u16 code; - unsigned char codes[2]; - unsigned char keybuf[6]; - int got_data = 0; - int ret; - int failures = 0; - unsigned char sendbuf[1] = { 0 }; - struct lirc_buffer *rbuf = ir->l.rbuf; - struct IR_rx *rx; - struct IR_tx *tx; - - if (lirc_buffer_full(rbuf)) { - dprintk("buffer overflow\n"); - return -EOVERFLOW; - } - - rx = get_ir_rx(ir); - if (rx == NULL) - return -ENXIO; - - /* Ensure our rx->c i2c_client remains valid for the duration */ - mutex_lock(&rx->client_lock); - if (rx->c == NULL) { - mutex_unlock(&rx->client_lock); - put_ir_rx(rx, false); - return -ENXIO; - } - - tx = get_ir_tx(ir); - - /* - * service the device as long as it is returning - * data and we have space - */ - do { - if (kthread_should_stop()) { - ret = -ENODATA; - break; - } - - /* - * Lock i2c bus for the duration. RX/TX chips interfere so - * this is worth it - */ - mutex_lock(&ir->ir_lock); - - if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); - ret = -ENODATA; - break; - } - - /* - * Send random "poll command" (?) Windows driver does this - * and it is a good point to detect chip failure. - */ - ret = i2c_master_send(rx->c, sendbuf, 1); - if (ret != 1) { - zilog_error("i2c_master_send failed with %d\n", ret); - if (failures >= 3) { - mutex_unlock(&ir->ir_lock); - zilog_error("unable to read from the IR chip " - "after 3 resets, giving up\n"); - break; - } - - /* Looks like the chip crashed, reset it */ - zilog_error("polling the IR receiver chip failed, " - "trying reset\n"); - - set_current_state(TASK_UNINTERRUPTIBLE); - if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); - ret = -ENODATA; - break; - } - schedule_timeout((100 * HZ + 999) / 1000); - if (tx != NULL) - tx->need_boot = 1; - - ++failures; - mutex_unlock(&ir->ir_lock); - ret = 0; - continue; - } - - if (kthread_should_stop()) { - mutex_unlock(&ir->ir_lock); - ret = -ENODATA; - break; - } - ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); - mutex_unlock(&ir->ir_lock); - if (ret != sizeof(keybuf)) { - zilog_error("i2c_master_recv failed with %d -- " - "keeping last read buffer\n", ret); - } else { - rx->b[0] = keybuf[3]; - rx->b[1] = keybuf[4]; - rx->b[2] = keybuf[5]; - dprintk("key (0x%02x/0x%02x)\n", rx->b[0], rx->b[1]); - } - - /* key pressed ? */ - if (rx->hdpvr_data_fmt) { - if (got_data && (keybuf[0] == 0x80)) { - ret = 0; - break; - } else if (got_data && (keybuf[0] == 0x00)) { - ret = -ENODATA; - break; - } - } else if ((rx->b[0] & 0x80) == 0) { - ret = got_data ? 0 : -ENODATA; - break; - } - - /* look what we have */ - code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2); - - codes[0] = (code >> 8) & 0xff; - codes[1] = code & 0xff; - - /* return it */ - lirc_buffer_write(rbuf, codes); - ++got_data; - ret = 0; - } while (!lirc_buffer_full(rbuf)); - - mutex_unlock(&rx->client_lock); - if (tx != NULL) - put_ir_tx(tx, false); - put_ir_rx(rx, false); - return ret; -} - -/* - * Main function of the polling thread -- from lirc_dev. - * We don't fit the LIRC model at all anymore. This is horrible, but - * basically we have a single RX/TX device with a nasty failure mode - * that needs to be accounted for across the pair. lirc lets us provide - * fops, but prevents us from using the internal polling, etc. if we do - * so. Hence the replication. Might be neater to extend the LIRC model - * to account for this but I'd think it's a very special case of seriously - * messed up hardware. - */ -static int lirc_thread(void *arg) -{ - struct IR *ir = arg; - struct lirc_buffer *rbuf = ir->l.rbuf; - - dprintk("poll thread started\n"); - - while (!kthread_should_stop()) { - set_current_state(TASK_INTERRUPTIBLE); - - /* if device not opened, we can sleep half a second */ - if (atomic_read(&ir->open_count) == 0) { - schedule_timeout(HZ/2); - continue; - } - - /* - * This is ~113*2 + 24 + jitter (2*repeat gap + code length). - * We use this interval as the chip resets every time you poll - * it (bad!). This is therefore just sufficient to catch all - * of the button presses. It makes the remote much more - * responsive. You can see the difference by running irw and - * holding down a button. With 100ms, the old polling - * interval, you'll notice breaks in the repeat sequence - * corresponding to lost keypresses. - */ - schedule_timeout((260 * HZ) / 1000); - if (kthread_should_stop()) - break; - if (!add_to_buf(ir)) - wake_up_interruptible(&rbuf->wait_poll); - } - - dprintk("poll thread ended\n"); - return 0; -} - -static int set_use_inc(void *data) -{ - return 0; -} - -static void set_use_dec(void *data) -{ - return; -} - -/* safe read of a uint32 (always network byte order) */ -static int read_uint32(unsigned char **data, - unsigned char *endp, unsigned int *val) -{ - if (*data + 4 > endp) - return 0; - *val = ((*data)[0] << 24) | ((*data)[1] << 16) | - ((*data)[2] << 8) | (*data)[3]; - *data += 4; - return 1; -} - -/* safe read of a uint8 */ -static int read_uint8(unsigned char **data, - unsigned char *endp, unsigned char *val) -{ - if (*data + 1 > endp) - return 0; - *val = *((*data)++); - return 1; -} - -/* safe skipping of N bytes */ -static int skip(unsigned char **data, - unsigned char *endp, unsigned int distance) -{ - if (*data + distance > endp) - return 0; - *data += distance; - return 1; -} - -/* decompress key data into the given buffer */ -static int get_key_data(unsigned char *buf, - unsigned int codeset, unsigned int key) -{ - unsigned char *data, *endp, *diffs, *key_block; - unsigned char keys, ndiffs, id; - unsigned int base, lim, pos, i; - - /* Binary search for the codeset */ - for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) { - pos = base + (lim >> 1); - data = tx_data->code_sets[pos]; - - if (!read_uint32(&data, tx_data->endp, &i)) - goto corrupt; - - if (i == codeset) - break; - else if (codeset > i) { - base = pos + 1; - --lim; - } - } - /* Not found? */ - if (!lim) - return -EPROTO; - - /* Set end of data block */ - endp = pos < tx_data->num_code_sets - 1 ? - tx_data->code_sets[pos + 1] : tx_data->endp; - - /* Read the block header */ - if (!read_uint8(&data, endp, &keys) || - !read_uint8(&data, endp, &ndiffs) || - ndiffs > TX_BLOCK_SIZE || keys == 0) - goto corrupt; - - /* Save diffs & skip */ - diffs = data; - if (!skip(&data, endp, ndiffs)) - goto corrupt; - - /* Read the id of the first key */ - if (!read_uint8(&data, endp, &id)) - goto corrupt; - - /* Unpack the first key's data */ - for (i = 0; i < TX_BLOCK_SIZE; ++i) { - if (tx_data->fixed[i] == -1) { - if (!read_uint8(&data, endp, &buf[i])) - goto corrupt; - } else { - buf[i] = (unsigned char)tx_data->fixed[i]; - } - } - - /* Early out key found/not found */ - if (key == id) - return 0; - if (keys == 1) - return -EPROTO; - - /* Sanity check */ - key_block = data; - if (!skip(&data, endp, (keys - 1) * (ndiffs + 1))) - goto corrupt; - - /* Binary search for the key */ - for (base = 0, lim = keys - 1; lim; lim >>= 1) { - /* Seek to block */ - unsigned char *key_data; - pos = base + (lim >> 1); - key_data = key_block + (ndiffs + 1) * pos; - - if (*key_data == key) { - /* skip key id */ - ++key_data; - - /* found, so unpack the diffs */ - for (i = 0; i < ndiffs; ++i) { - unsigned char val; - if (!read_uint8(&key_data, endp, &val) || - diffs[i] >= TX_BLOCK_SIZE) - goto corrupt; - buf[diffs[i]] = val; - } - - return 0; - } else if (key > *key_data) { - base = pos + 1; - --lim; - } - } - /* Key not found */ - return -EPROTO; - -corrupt: - zilog_error("firmware is corrupt\n"); - return -EFAULT; -} - -/* send a block of data to the IR TX device */ -static int send_data_block(struct IR_tx *tx, unsigned char *data_block) -{ - int i, j, ret; - unsigned char buf[5]; - - for (i = 0; i < TX_BLOCK_SIZE;) { - int tosend = TX_BLOCK_SIZE - i; - if (tosend > 4) - tosend = 4; - buf[0] = (unsigned char)(i + 1); - for (j = 0; j < tosend; ++j) - buf[1 + j] = data_block[i + j]; - dprintk("%02x %02x %02x %02x %02x", - buf[0], buf[1], buf[2], buf[3], buf[4]); - ret = i2c_master_send(tx->c, buf, tosend + 1); - if (ret != tosend + 1) { - zilog_error("i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - i += tosend; - } - return 0; -} - -/* send boot data to the IR TX device */ -static int send_boot_data(struct IR_tx *tx) -{ - int ret, i; - unsigned char buf[4]; - - /* send the boot block */ - ret = send_data_block(tx, tx_data->boot_data); - if (ret != 0) - return ret; - - /* Hit the go button to activate the new boot data */ - buf[0] = 0x00; - buf[1] = 0x20; - ret = i2c_master_send(tx->c, buf, 2); - if (ret != 2) { - zilog_error("i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* - * Wait for zilog to settle after hitting go post boot block upload. - * Without this delay, the HD-PVR and HVR-1950 both return an -EIO - * upon attempting to get firmware revision, and tx probe thus fails. - */ - for (i = 0; i < 10; i++) { - ret = i2c_master_send(tx->c, buf, 1); - if (ret == 1) - break; - udelay(100); - } - - if (ret != 1) { - zilog_error("i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Here comes the firmware version... (hopefully) */ - ret = i2c_master_recv(tx->c, buf, 4); - if (ret != 4) { - zilog_error("i2c_master_recv failed with %d\n", ret); - return 0; - } - if ((buf[0] != 0x80) && (buf[0] != 0xa0)) { - zilog_error("unexpected IR TX init response: %02x\n", buf[0]); - return 0; - } - zilog_notify("Zilog/Hauppauge IR blaster firmware version " - "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]); - - return 0; -} - -/* unload "firmware", lock held */ -static void fw_unload_locked(void) -{ - if (tx_data) { - if (tx_data->code_sets) - vfree(tx_data->code_sets); - - if (tx_data->datap) - vfree(tx_data->datap); - - vfree(tx_data); - tx_data = NULL; - dprintk("successfully unloaded IR blaster firmware\n"); - } -} - -/* unload "firmware" for the IR TX device */ -static void fw_unload(void) -{ - mutex_lock(&tx_data_lock); - fw_unload_locked(); - mutex_unlock(&tx_data_lock); -} - -/* load "firmware" for the IR TX device */ -static int fw_load(struct IR_tx *tx) -{ - int ret; - unsigned int i; - unsigned char *data, version, num_global_fixed; - const struct firmware *fw_entry; - - /* Already loaded? */ - mutex_lock(&tx_data_lock); - if (tx_data) { - ret = 0; - goto out; - } - - /* Request codeset data file */ - ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev); - if (ret != 0) { - zilog_error("firmware haup-ir-blaster.bin not available " - "(%d)\n", ret); - ret = ret < 0 ? ret : -EFAULT; - goto out; - } - dprintk("firmware of size %zu loaded\n", fw_entry->size); - - /* Parse the file */ - tx_data = vmalloc(sizeof(*tx_data)); - if (tx_data == NULL) { - zilog_error("out of memory\n"); - release_firmware(fw_entry); - ret = -ENOMEM; - goto out; - } - tx_data->code_sets = NULL; - - /* Copy the data so hotplug doesn't get confused and timeout */ - tx_data->datap = vmalloc(fw_entry->size); - if (tx_data->datap == NULL) { - zilog_error("out of memory\n"); - release_firmware(fw_entry); - vfree(tx_data); - ret = -ENOMEM; - goto out; - } - memcpy(tx_data->datap, fw_entry->data, fw_entry->size); - tx_data->endp = tx_data->datap + fw_entry->size; - release_firmware(fw_entry); fw_entry = NULL; - - /* Check version */ - data = tx_data->datap; - if (!read_uint8(&data, tx_data->endp, &version)) - goto corrupt; - if (version != 1) { - zilog_error("unsupported code set file version (%u, expected" - "1) -- please upgrade to a newer driver", - version); - fw_unload_locked(); - ret = -EFAULT; - goto out; - } - - /* Save boot block for later */ - tx_data->boot_data = data; - if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE)) - goto corrupt; - - if (!read_uint32(&data, tx_data->endp, - &tx_data->num_code_sets)) - goto corrupt; - - dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets); - - tx_data->code_sets = vmalloc( - tx_data->num_code_sets * sizeof(char *)); - if (tx_data->code_sets == NULL) { - fw_unload_locked(); - ret = -ENOMEM; - goto out; - } - - for (i = 0; i < TX_BLOCK_SIZE; ++i) - tx_data->fixed[i] = -1; - - /* Read global fixed data template */ - if (!read_uint8(&data, tx_data->endp, &num_global_fixed) || - num_global_fixed > TX_BLOCK_SIZE) - goto corrupt; - for (i = 0; i < num_global_fixed; ++i) { - unsigned char pos, val; - if (!read_uint8(&data, tx_data->endp, &pos) || - !read_uint8(&data, tx_data->endp, &val) || - pos >= TX_BLOCK_SIZE) - goto corrupt; - tx_data->fixed[pos] = (int)val; - } - - /* Filch out the position of each code set */ - for (i = 0; i < tx_data->num_code_sets; ++i) { - unsigned int id; - unsigned char keys; - unsigned char ndiffs; - - /* Save the codeset position */ - tx_data->code_sets[i] = data; - - /* Read header */ - if (!read_uint32(&data, tx_data->endp, &id) || - !read_uint8(&data, tx_data->endp, &keys) || - !read_uint8(&data, tx_data->endp, &ndiffs) || - ndiffs > TX_BLOCK_SIZE || keys == 0) - goto corrupt; - - /* skip diff positions */ - if (!skip(&data, tx_data->endp, ndiffs)) - goto corrupt; - - /* - * After the diffs we have the first key id + data - - * global fixed - */ - if (!skip(&data, tx_data->endp, - 1 + TX_BLOCK_SIZE - num_global_fixed)) - goto corrupt; - - /* Then we have keys-1 blocks of key id+diffs */ - if (!skip(&data, tx_data->endp, - (ndiffs + 1) * (keys - 1))) - goto corrupt; - } - ret = 0; - goto out; - -corrupt: - zilog_error("firmware is corrupt\n"); - fw_unload_locked(); - ret = -EFAULT; - -out: - mutex_unlock(&tx_data_lock); - return ret; -} - -/* copied from lirc_dev */ -static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) -{ - struct IR *ir = filep->private_data; - struct IR_rx *rx; - struct lirc_buffer *rbuf = ir->l.rbuf; - int ret = 0, written = 0, retries = 0; - unsigned int m; - DECLARE_WAITQUEUE(wait, current); - - dprintk("read called\n"); - if (n % rbuf->chunk_size) { - dprintk("read result = -EINVAL\n"); - return -EINVAL; - } - - rx = get_ir_rx(ir); - if (rx == NULL) - return -ENXIO; - - /* - * we add ourselves to the task queue before buffer check - * to avoid losing scan code (in case when queue is awaken somewhere - * between while condition checking and scheduling) - */ - add_wait_queue(&rbuf->wait_poll, &wait); - set_current_state(TASK_INTERRUPTIBLE); - - /* - * while we didn't provide 'length' bytes, device is opened in blocking - * mode and 'copy_to_user' is happy, wait for data. - */ - while (written < n && ret == 0) { - if (lirc_buffer_empty(rbuf)) { - /* - * According to the read(2) man page, 'written' can be - * returned as less than 'n', instead of blocking - * again, returning -EWOULDBLOCK, or returning - * -ERESTARTSYS - */ - if (written) - break; - if (filep->f_flags & O_NONBLOCK) { - ret = -EWOULDBLOCK; - break; - } - if (signal_pending(current)) { - ret = -ERESTARTSYS; - break; - } - schedule(); - set_current_state(TASK_INTERRUPTIBLE); - } else { - unsigned char buf[rbuf->chunk_size]; - m = lirc_buffer_read(rbuf, buf); - if (m == rbuf->chunk_size) { - ret = copy_to_user((void *)outbuf+written, buf, - rbuf->chunk_size); - written += rbuf->chunk_size; - } else { - retries++; - } - if (retries >= 5) { - zilog_error("Buffer read failed!\n"); - ret = -EIO; - } - } - } - - remove_wait_queue(&rbuf->wait_poll, &wait); - put_ir_rx(rx, false); - set_current_state(TASK_RUNNING); - - dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); - - return ret ? ret : written; -} - -/* send a keypress to the IR TX device */ -static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) -{ - unsigned char data_block[TX_BLOCK_SIZE]; - unsigned char buf[2]; - int i, ret; - - /* Get data for the codeset/key */ - ret = get_key_data(data_block, code, key); - - if (ret == -EPROTO) { - zilog_error("failed to get data for code %u, key %u -- check " - "lircd.conf entries\n", code, key); - return ret; - } else if (ret != 0) - return ret; - - /* Send the data block */ - ret = send_data_block(tx, data_block); - if (ret != 0) - return ret; - - /* Send data block length? */ - buf[0] = 0x00; - buf[1] = 0x40; - ret = i2c_master_send(tx->c, buf, 2); - if (ret != 2) { - zilog_error("i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Give the z8 a moment to process data block */ - for (i = 0; i < 10; i++) { - ret = i2c_master_send(tx->c, buf, 1); - if (ret == 1) - break; - udelay(100); - } - - if (ret != 1) { - zilog_error("i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Send finished download? */ - ret = i2c_master_recv(tx->c, buf, 1); - if (ret != 1) { - zilog_error("i2c_master_recv failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - if (buf[0] != 0xA0) { - zilog_error("unexpected IR TX response #1: %02x\n", - buf[0]); - return -EFAULT; - } - - /* Send prepare command? */ - buf[0] = 0x00; - buf[1] = 0x80; - ret = i2c_master_send(tx->c, buf, 2); - if (ret != 2) { - zilog_error("i2c_master_send failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* - * The sleep bits aren't necessary on the HD PVR, and in fact, the - * last i2c_master_recv always fails with a -5, so for now, we're - * going to skip this whole mess and say we're done on the HD PVR - */ - if (!tx->post_tx_ready_poll) { - dprintk("sent code %u, key %u\n", code, key); - return 0; - } - - /* - * This bit NAKs until the device is ready, so we retry it - * sleeping a bit each time. This seems to be what the windows - * driver does, approximately. - * Try for up to 1s. - */ - for (i = 0; i < 20; ++i) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((50 * HZ + 999) / 1000); - ret = i2c_master_send(tx->c, buf, 1); - if (ret == 1) - break; - dprintk("NAK expected: i2c_master_send " - "failed with %d (try %d)\n", ret, i+1); - } - if (ret != 1) { - zilog_error("IR TX chip never got ready: last i2c_master_send " - "failed with %d\n", ret); - return ret < 0 ? ret : -EFAULT; - } - - /* Seems to be an 'ok' response */ - i = i2c_master_recv(tx->c, buf, 1); - if (i != 1) { - zilog_error("i2c_master_recv failed with %d\n", ret); - return -EFAULT; - } - if (buf[0] != 0x80) { - zilog_error("unexpected IR TX response #2: %02x\n", buf[0]); - return -EFAULT; - } - - /* Oh good, it worked */ - dprintk("sent code %u, key %u\n", code, key); - return 0; -} - -/* - * Write a code to the device. We take in a 32-bit number (an int) and then - * decode this to a codeset/key index. The key data is then decompressed and - * sent to the device. We have a spin lock as per i2c documentation to prevent - * multiple concurrent sends which would probably cause the device to explode. - */ -static ssize_t write(struct file *filep, const char *buf, size_t n, - loff_t *ppos) -{ - struct IR *ir = filep->private_data; - struct IR_tx *tx; - size_t i; - int failures = 0; - - /* Validate user parameters */ - if (n % sizeof(int)) - return -EINVAL; - - /* Get a struct IR_tx reference */ - tx = get_ir_tx(ir); - if (tx == NULL) - return -ENXIO; - - /* Ensure our tx->c i2c_client remains valid for the duration */ - mutex_lock(&tx->client_lock); - if (tx->c == NULL) { - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return -ENXIO; - } - - /* Lock i2c bus for the duration */ - mutex_lock(&ir->ir_lock); - - /* Send each keypress */ - for (i = 0; i < n;) { - int ret = 0; - int command; - - if (copy_from_user(&command, buf + i, sizeof(command))) { - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return -EFAULT; - } - - /* Send boot data first if required */ - if (tx->need_boot == 1) { - /* Make sure we have the 'firmware' loaded, first */ - ret = fw_load(tx); - if (ret != 0) { - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - if (ret != -ENOMEM) - ret = -EIO; - return ret; - } - /* Prep the chip for transmitting codes */ - ret = send_boot_data(tx); - if (ret == 0) - tx->need_boot = 0; - } - - /* Send the code */ - if (ret == 0) { - ret = send_code(tx, (unsigned)command >> 16, - (unsigned)command & 0xFFFF); - if (ret == -EPROTO) { - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return ret; - } - } - - /* - * Hmm, a failure. If we've had a few then give up, otherwise - * try a reset - */ - if (ret != 0) { - /* Looks like the chip crashed, reset it */ - zilog_error("sending to the IR transmitter chip " - "failed, trying reset\n"); - - if (failures >= 3) { - zilog_error("unable to send to the IR chip " - "after 3 resets, giving up\n"); - mutex_unlock(&ir->ir_lock); - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - return ret; - } - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout((100 * HZ + 999) / 1000); - tx->need_boot = 1; - ++failures; - } else - i += sizeof(int); - } - - /* Release i2c bus */ - mutex_unlock(&ir->ir_lock); - - mutex_unlock(&tx->client_lock); - - /* Give back our struct IR_tx reference */ - put_ir_tx(tx, false); - - /* All looks good */ - return n; -} - -/* copied from lirc_dev */ -static unsigned int poll(struct file *filep, poll_table *wait) -{ - struct IR *ir = filep->private_data; - struct IR_rx *rx; - struct lirc_buffer *rbuf = ir->l.rbuf; - unsigned int ret; - - dprintk("poll called\n"); - - rx = get_ir_rx(ir); - if (rx == NULL) { - /* - * Revisit this, if our poll function ever reports writeable - * status for Tx - */ - dprintk("poll result = POLLERR\n"); - return POLLERR; - } - - /* - * Add our lirc_buffer's wait_queue to the poll_table. A wake up on - * that buffer's wait queue indicates we may have a new poll status. - */ - poll_wait(filep, &rbuf->wait_poll, wait); - - /* Indicate what ops could happen immediately without blocking */ - ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM); - - dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none"); - return ret; -} - -static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) -{ - struct IR *ir = filep->private_data; - int result; - unsigned long mode, features; - - features = ir->l.features; - - switch (cmd) { - case LIRC_GET_LENGTH: - result = put_user((unsigned long)13, - (unsigned long *)arg); - break; - case LIRC_GET_FEATURES: - result = put_user(features, (unsigned long *) arg); - break; - case LIRC_GET_REC_MODE: - if (!(features&LIRC_CAN_REC_MASK)) - return -ENOSYS; - - result = put_user(LIRC_REC2MODE - (features&LIRC_CAN_REC_MASK), - (unsigned long *)arg); - break; - case LIRC_SET_REC_MODE: - if (!(features&LIRC_CAN_REC_MASK)) - return -ENOSYS; - - result = get_user(mode, (unsigned long *)arg); - if (!result && !(LIRC_MODE2REC(mode) & features)) - result = -EINVAL; - break; - case LIRC_GET_SEND_MODE: - if (!(features&LIRC_CAN_SEND_MASK)) - return -ENOSYS; - - result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); - break; - case LIRC_SET_SEND_MODE: - if (!(features&LIRC_CAN_SEND_MASK)) - return -ENOSYS; - - result = get_user(mode, (unsigned long *) arg); - if (!result && mode != LIRC_MODE_PULSE) - return -EINVAL; - break; - default: - return -EINVAL; - } - return result; -} - -static struct IR *get_ir_device_by_minor(unsigned int minor) -{ - struct IR *ir; - struct IR *ret = NULL; - - mutex_lock(&ir_devices_lock); - - if (!list_empty(&ir_devices_list)) { - list_for_each_entry(ir, &ir_devices_list, list) { - if (ir->l.minor == minor) { - ret = get_ir_device(ir, true); - break; - } - } - } - - mutex_unlock(&ir_devices_lock); - return ret; -} - -/* - * Open the IR device. Get hold of our IR structure and - * stash it in private_data for the file - */ -static int open(struct inode *node, struct file *filep) -{ - struct IR *ir; - unsigned int minor = MINOR(node->i_rdev); - - /* find our IR struct */ - ir = get_ir_device_by_minor(minor); - - if (ir == NULL) - return -ENODEV; - - atomic_inc(&ir->open_count); - - /* stash our IR struct */ - filep->private_data = ir; - - nonseekable_open(node, filep); - return 0; -} - -/* Close the IR device */ -static int close(struct inode *node, struct file *filep) -{ - /* find our IR struct */ - struct IR *ir = filep->private_data; - if (ir == NULL) { - zilog_error("close: no private_data attached to the file!\n"); - return -ENODEV; - } - - atomic_dec(&ir->open_count); - - put_ir_device(ir, false); - return 0; -} - -static int ir_remove(struct i2c_client *client); -static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); - -#define ID_FLAG_TX 0x01 -#define ID_FLAG_HDPVR 0x02 - -static const struct i2c_device_id ir_transceiver_id[] = { - { "ir_tx_z8f0811_haup", ID_FLAG_TX }, - { "ir_rx_z8f0811_haup", 0 }, - { "ir_tx_z8f0811_hdpvr", ID_FLAG_HDPVR | ID_FLAG_TX }, - { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR }, - { } -}; - -static struct i2c_driver driver = { - .driver = { - .owner = THIS_MODULE, - .name = "Zilog/Hauppauge i2c IR", - }, - .probe = ir_probe, - .remove = ir_remove, - .id_table = ir_transceiver_id, -}; - -static const struct file_operations lirc_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = read, - .write = write, - .poll = poll, - .unlocked_ioctl = ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = ioctl, -#endif - .open = open, - .release = close -}; - -static struct lirc_driver lirc_template = { - .name = "lirc_zilog", - .minor = -1, - .code_length = 13, - .buffer_size = BUFLEN / 2, - .sample_rate = 0, /* tell lirc_dev to not start its own kthread */ - .chunk_size = 2, - .set_use_inc = set_use_inc, - .set_use_dec = set_use_dec, - .fops = &lirc_fops, - .owner = THIS_MODULE, -}; - -static int ir_remove(struct i2c_client *client) -{ - if (strncmp("ir_tx_z8", client->name, 8) == 0) { - struct IR_tx *tx = i2c_get_clientdata(client); - if (tx != NULL) { - mutex_lock(&tx->client_lock); - tx->c = NULL; - mutex_unlock(&tx->client_lock); - put_ir_tx(tx, false); - } - } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { - struct IR_rx *rx = i2c_get_clientdata(client); - if (rx != NULL) { - mutex_lock(&rx->client_lock); - rx->c = NULL; - mutex_unlock(&rx->client_lock); - put_ir_rx(rx, false); - } - } - return 0; -} - - -/* ir_devices_lock must be held */ -static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter) -{ - struct IR *ir; - - if (list_empty(&ir_devices_list)) - return NULL; - - list_for_each_entry(ir, &ir_devices_list, list) - if (ir->adapter == adapter) { - get_ir_device(ir, true); - return ir; - } - - return NULL; -} - -static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) -{ - struct IR *ir; - struct IR_tx *tx; - struct IR_rx *rx; - struct i2c_adapter *adap = client->adapter; - int ret; - bool tx_probe = false; - - dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n", - __func__, id->name, adap->nr, adap->name, client->addr); - - /* - * The IR receiver is at i2c address 0x71. - * The IR transmitter is at i2c address 0x70. - */ - - if (id->driver_data & ID_FLAG_TX) - tx_probe = true; - else if (tx_only) /* module option */ - return -ENXIO; - - zilog_info("probing IR %s on %s (i2c-%d)\n", - tx_probe ? "Tx" : "Rx", adap->name, adap->nr); - - mutex_lock(&ir_devices_lock); - - /* Use a single struct IR instance for both the Rx and Tx functions */ - ir = get_ir_device_by_adapter(adap); - if (ir == NULL) { - ir = kzalloc(sizeof(struct IR), GFP_KERNEL); - if (ir == NULL) { - ret = -ENOMEM; - goto out_no_ir; - } - kref_init(&ir->ref); - - /* store for use in ir_probe() again, and open() later on */ - INIT_LIST_HEAD(&ir->list); - list_add_tail(&ir->list, &ir_devices_list); - - ir->adapter = adap; - mutex_init(&ir->ir_lock); - atomic_set(&ir->open_count, 0); - spin_lock_init(&ir->tx_ref_lock); - spin_lock_init(&ir->rx_ref_lock); - - /* set lirc_dev stuff */ - memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); - /* - * FIXME this is a pointer reference to us, but no refcount. - * - * This OK for now, since lirc_dev currently won't touch this - * buffer as we provide our own lirc_fops. - * - * Currently our own lirc_fops rely on this ir->l.rbuf pointer - */ - ir->l.rbuf = &ir->rbuf; - ir->l.dev = &adap->dev; - ret = lirc_buffer_init(ir->l.rbuf, - ir->l.chunk_size, ir->l.buffer_size); - if (ret) - goto out_put_ir; - } - - if (tx_probe) { - /* Get the IR_rx instance for later, if already allocated */ - rx = get_ir_rx(ir); - - /* Set up a struct IR_tx instance */ - tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); - if (tx == NULL) { - ret = -ENOMEM; - goto out_put_xx; - } - kref_init(&tx->ref); - ir->tx = tx; - - ir->l.features |= LIRC_CAN_SEND_PULSE; - mutex_init(&tx->client_lock); - tx->c = client; - tx->need_boot = 1; - tx->post_tx_ready_poll = - (id->driver_data & ID_FLAG_HDPVR) ? false : true; - - /* An ir ref goes to the struct IR_tx instance */ - tx->ir = get_ir_device(ir, true); - - /* A tx ref goes to the i2c_client */ - i2c_set_clientdata(client, get_ir_tx(ir)); - - /* - * Load the 'firmware'. We do this before registering with - * lirc_dev, so the first firmware load attempt does not happen - * after a open() or write() call on the device. - * - * Failure here is not deemed catastrophic, so the receiver will - * still be usable. Firmware load will be retried in write(), - * if it is needed. - */ - fw_load(tx); - - /* Proceed only if the Rx client is also ready or not needed */ - if (rx == NULL && !tx_only) { - zilog_info("probe of IR Tx on %s (i2c-%d) done. Waiting" - " on IR Rx.\n", adap->name, adap->nr); - goto out_ok; - } - } else { - /* Get the IR_tx instance for later, if already allocated */ - tx = get_ir_tx(ir); - - /* Set up a struct IR_rx instance */ - rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); - if (rx == NULL) { - ret = -ENOMEM; - goto out_put_xx; - } - kref_init(&rx->ref); - ir->rx = rx; - - ir->l.features |= LIRC_CAN_REC_LIRCCODE; - mutex_init(&rx->client_lock); - rx->c = client; - rx->hdpvr_data_fmt = - (id->driver_data & ID_FLAG_HDPVR) ? true : false; - - /* An ir ref goes to the struct IR_rx instance */ - rx->ir = get_ir_device(ir, true); - - /* An rx ref goes to the i2c_client */ - i2c_set_clientdata(client, get_ir_rx(ir)); - - /* - * Start the polling thread. - * It will only perform an empty loop around schedule_timeout() - * until we register with lirc_dev and the first user open() - */ - /* An ir ref goes to the new rx polling kthread */ - rx->task = kthread_run(lirc_thread, get_ir_device(ir, true), - "zilog-rx-i2c-%d", adap->nr); - if (IS_ERR(rx->task)) { - ret = PTR_ERR(rx->task); - zilog_error("%s: could not start IR Rx polling thread" - "\n", __func__); - /* Failed kthread, so put back the ir ref */ - put_ir_device(ir, true); - /* Failure exit, so put back rx ref from i2c_client */ - i2c_set_clientdata(client, NULL); - put_ir_rx(rx, true); - ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; - goto out_put_xx; - } - - /* Proceed only if the Tx client is also ready */ - if (tx == NULL) { - zilog_info("probe of IR Rx on %s (i2c-%d) done. Waiting" - " on IR Tx.\n", adap->name, adap->nr); - goto out_ok; - } - } - - /* register with lirc */ - ir->l.minor = minor; /* module option: user requested minor number */ - ir->l.minor = lirc_register_driver(&ir->l); - if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { - zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n", - __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); - ret = -EBADRQC; - goto out_put_xx; - } - zilog_info("IR unit on %s (i2c-%d) registered as lirc%d and ready\n", - adap->name, adap->nr, ir->l.minor); - -out_ok: - if (rx != NULL) - put_ir_rx(rx, true); - if (tx != NULL) - put_ir_tx(tx, true); - put_ir_device(ir, true); - zilog_info("probe of IR %s on %s (i2c-%d) done\n", - tx_probe ? "Tx" : "Rx", adap->name, adap->nr); - mutex_unlock(&ir_devices_lock); - return 0; - -out_put_xx: - if (rx != NULL) - put_ir_rx(rx, true); - if (tx != NULL) - put_ir_tx(tx, true); -out_put_ir: - put_ir_device(ir, true); -out_no_ir: - zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n", - __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, - ret); - mutex_unlock(&ir_devices_lock); - return ret; -} - -static int __init zilog_init(void) -{ - int ret; - - zilog_notify("Zilog/Hauppauge IR driver initializing\n"); - - mutex_init(&tx_data_lock); - - request_module("firmware_class"); - - ret = i2c_add_driver(&driver); - if (ret) - zilog_error("initialization failed\n"); - else - zilog_notify("initialization complete\n"); - - return ret; -} - -static void __exit zilog_exit(void) -{ - i2c_del_driver(&driver); - /* if loaded */ - fw_unload(); - zilog_notify("Zilog/Hauppauge IR driver unloaded\n"); -} - -module_init(zilog_init); -module_exit(zilog_exit); - -MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)"); -MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, " - "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, " - "Andy Walls"); -MODULE_LICENSE("GPL"); -/* for compat with old name, which isn't all that accurate anymore */ -MODULE_ALIAS("lirc_pvr150"); - -module_param(minor, int, 0444); -MODULE_PARM_DESC(minor, "Preferred minor device number"); - -module_param(debug, bool, 0644); -MODULE_PARM_DESC(debug, "Enable debugging messages"); - -module_param(tx_only, bool, 0644); -MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function"); diff --git a/drivers/staging/media/Kconfig b/drivers/staging/media/Kconfig new file mode 100644 index 000000000000..7e5caa39ed3f --- /dev/null +++ b/drivers/staging/media/Kconfig @@ -0,0 +1,37 @@ +menuconfig STAGING_MEDIA + bool "Media staging drivers" + default n + ---help--- + This option allows you to select a number of media drivers that + don't have the "normal" Linux kernel quality level. + Most of them don't follow properly the V4L, DVB and/or RC API's, + so, they won't likely work fine with the existing applications. + That also means that, one fixed, their API's will change to match + the existing ones. + + If you wish to work on these drivers, to help improve them, or + to report problems you have with them, please use the + linux-media@vger.kernel.org mailing list. + + If in doubt, say N here. + + +if STAGING_MEDIA + +# Please keep them in alphabetic order +source "drivers/staging/media/as102/Kconfig" + +source "drivers/staging/media/cxd2099/Kconfig" + +source "drivers/staging/media/dt3155v4l/Kconfig" + +source "drivers/staging/media/easycap/Kconfig" + +source "drivers/staging/media/go7007/Kconfig" + +source "drivers/staging/media/solo6x10/Kconfig" + +# Keep LIRC at the end, as it has sub-menus +source "drivers/staging/media/lirc/Kconfig" + +endif diff --git a/drivers/staging/media/Makefile b/drivers/staging/media/Makefile new file mode 100644 index 000000000000..c69124cdb0d3 --- /dev/null +++ b/drivers/staging/media/Makefile @@ -0,0 +1,7 @@ +obj-$(CONFIG_DVB_AS102) += as102/ +obj-$(CONFIG_DVB_CXD2099) += cxd2099/ +obj-$(CONFIG_EASYCAP) += easycap/ +obj-$(CONFIG_LIRC_STAGING) += lirc/ +obj-$(CONFIG_SOLO6X10) += solo6x10/ +obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l/ +obj-$(CONFIG_VIDEO_GO7007) += go7007/ diff --git a/drivers/staging/media/cxd2099/Kconfig b/drivers/staging/media/cxd2099/Kconfig new file mode 100644 index 000000000000..b48aefddc84c --- /dev/null +++ b/drivers/staging/media/cxd2099/Kconfig @@ -0,0 +1,12 @@ +config DVB_CXD2099 + tristate "CXD2099AR Common Interface driver" + depends on DVB_CORE && PCI && I2C + ---help--- + Support for the CI module found on cards based on + - Micronas ngene PCIe bridge: cineS2 etc. + - Digital Devices PCIe bridge: Octopus series + + For now, data is passed through '/dev/dvb/adapterX/sec0': + - Encrypted data must be written to 'sec0'. + - Decrypted data can be read from 'sec0'. + - Setup the CAM using device 'ca0'. diff --git a/drivers/staging/media/cxd2099/Makefile b/drivers/staging/media/cxd2099/Makefile new file mode 100644 index 000000000000..64cfc77be357 --- /dev/null +++ b/drivers/staging/media/cxd2099/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_DVB_CXD2099) += cxd2099.o + +ccflags-y += -Idrivers/media/dvb/dvb-core/ +ccflags-y += -Idrivers/media/dvb/frontends/ +ccflags-y += -Idrivers/media/common/tuners/ diff --git a/drivers/staging/media/cxd2099/TODO b/drivers/staging/media/cxd2099/TODO new file mode 100644 index 000000000000..375bb6f8ee2c --- /dev/null +++ b/drivers/staging/media/cxd2099/TODO @@ -0,0 +1,12 @@ +For now, data is passed through '/dev/dvb/adapterX/sec0': + - Encrypted data must be written to 'sec0'. + - Decrypted data can be read from 'sec0'. + - Setup the CAM using device 'ca0'. + +But this is wrong. There are some discussions about the proper way for +doing it, as seen at: + http://www.mail-archive.com/linux-media@vger.kernel.org/msg22196.html + +While there's no proper fix for it, the driver should be kept in staging. + +Patches should be submitted to: linux-media@vger.kernel.org. diff --git a/drivers/staging/media/cxd2099/cxd2099.c b/drivers/staging/media/cxd2099/cxd2099.c new file mode 100644 index 000000000000..1c04185bcfd7 --- /dev/null +++ b/drivers/staging/media/cxd2099/cxd2099.c @@ -0,0 +1,716 @@ +/* + * cxd2099.c: Driver for the CXD2099AR Common Interface Controller + * + * 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 "cxd2099.h" + +#define MAX_BUFFER_SIZE 248 + +struct cxd { + struct dvb_ca_en50221 en; + + struct i2c_adapter *i2c; + struct cxd2099_cfg cfg; + + u8 regs[0x23]; + u8 lastaddress; + u8 clk_reg_f; + u8 clk_reg_b; + int mode; + int ready; + int dr; + int slot_stat; + + u8 amem[1024]; + int amem_read; + + int cammode; + struct mutex lock; +}; + +static int i2c_write_reg(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 data) +{ + u8 m[2] = {reg, data}; + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = m, .len = 2}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C register %02x@%02x!\n", + reg, adr); + return -1; + } + return 0; +} + +static int i2c_write(struct i2c_adapter *adapter, u8 adr, + u8 *data, u8 len) +{ + struct i2c_msg msg = {.addr = adr, .flags = 0, .buf = data, .len = len}; + + if (i2c_transfer(adapter, &msg, 1) != 1) { + printk(KERN_ERR "Failed to write to I2C!\n"); + return -1; + } + return 0; +} + +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} }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read_reg\n"); + return -1; + } + return 0; +} + +static int i2c_read(struct i2c_adapter *adapter, u8 adr, + u8 reg, u8 *data, u8 n) +{ + struct i2c_msg msgs[2] = {{.addr = adr, .flags = 0, + .buf = ®, .len = 1}, + {.addr = adr, .flags = I2C_M_RD, + .buf = data, .len = n} }; + + if (i2c_transfer(adapter, msgs, 2) != 2) { + printk(KERN_ERR "error in i2c_read\n"); + return -1; + } + return 0; +} + +static int read_block(struct cxd *ci, u8 adr, u8 *data, u8 n) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + status = i2c_read(ci->i2c, ci->cfg.adr, 1, data, n); + } + return status; +} + +static int read_reg(struct cxd *ci, u8 reg, u8 *val) +{ + return read_block(ci, reg, val, 1); +} + + +static int read_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = {2, address & 0xff, address >> 8}; + + status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n); + return status; +} + +static int write_pccard(struct cxd *ci, u16 address, u8 *data, u8 n) +{ + int status; + u8 addr[3] = {2, address & 0xff, address >> 8}; + + status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); + if (!status) { + u8 buf[256] = {3}; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->cfg.adr, buf, n+1); + } + return status; +} + +static int read_io(struct cxd *ci, u16 address, u8 *val) +{ + int status; + u8 addr[3] = {2, address & 0xff, address >> 8}; + + status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->cfg.adr, 3, val, 1); + return status; +} + +static int write_io(struct cxd *ci, u16 address, u8 val) +{ + int status; + u8 addr[3] = {2, address & 0xff, address >> 8}; + u8 buf[2] = {3, val}; + + status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); + if (!status) + status = i2c_write(ci->i2c, ci->cfg.adr, buf, 2); + return status; +} + +#if 0 +static int read_io_data(struct cxd *ci, u8 *data, u8 n) +{ + int status; + u8 addr[3] = { 2, 0, 0 }; + + status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); + if (!status) + status = i2c_read(ci->i2c, ci->cfg.adr, 3, data, n); + return 0; +} + +static int write_io_data(struct cxd *ci, u8 *data, u8 n) +{ + int status; + u8 addr[3] = {2, 0, 0}; + + status = i2c_write(ci->i2c, ci->cfg.adr, addr, 3); + if (!status) { + u8 buf[256] = {3}; + memcpy(buf+1, data, n); + status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1); + } + return 0; +} +#endif + +static int write_regm(struct cxd *ci, u8 reg, u8 val, u8 mask) +{ + int status; + + status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, reg); + if (!status && reg >= 6 && reg <= 8 && mask != 0xff) + status = i2c_read_reg(ci->i2c, ci->cfg.adr, 1, &ci->regs[reg]); + ci->regs[reg] = (ci->regs[reg] & (~mask)) | val; + if (!status) { + ci->lastaddress = reg; + status = i2c_write_reg(ci->i2c, ci->cfg.adr, 1, ci->regs[reg]); + } + if (reg == 0x20) + ci->regs[reg] &= 0x7f; + return status; +} + +static int write_reg(struct cxd *ci, u8 reg, u8 val) +{ + return write_regm(ci, reg, val, 0xff); +} + +#ifdef BUFFER_MODE +static int write_block(struct cxd *ci, u8 adr, u8 *data, int n) +{ + int status; + u8 buf[256] = {1}; + + status = i2c_write_reg(ci->i2c, ci->cfg.adr, 0, adr); + if (!status) { + ci->lastaddress = adr; + memcpy(buf + 1, data, n); + status = i2c_write(ci->i2c, ci->cfg.adr, buf, n + 1); + } + return status; +} +#endif + +static void set_mode(struct cxd *ci, int mode) +{ + if (mode == ci->mode) + return; + + switch (mode) { + case 0x00: /* IO mem */ + write_regm(ci, 0x06, 0x00, 0x07); + break; + case 0x01: /* ATT mem */ + write_regm(ci, 0x06, 0x02, 0x07); + break; + default: + break; + } + ci->mode = mode; +} + +static void cam_mode(struct cxd *ci, int mode) +{ + if (mode == ci->cammode) + return; + + switch (mode) { + case 0x00: + write_regm(ci, 0x20, 0x80, 0x80); + break; + case 0x01: +#ifdef BUFFER_MODE + if (!ci->en.read_data) + return; + printk(KERN_INFO "enable cam buffer mode\n"); + /* write_reg(ci, 0x0d, 0x00); */ + /* write_reg(ci, 0x0e, 0x01); */ + write_regm(ci, 0x08, 0x40, 0x40); + /* read_reg(ci, 0x12, &dummy); */ + write_regm(ci, 0x08, 0x80, 0x80); +#endif + break; + default: + break; + } + ci->cammode = mode; +} + + + +static int init(struct cxd *ci) +{ + int status; + + mutex_lock(&ci->lock); + ci->mode = -1; + do { + status = write_reg(ci, 0x00, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x01, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x02, 0x10); + if (status < 0) + break; + status = write_reg(ci, 0x03, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x05, 0xFF); + if (status < 0) + break; + status = write_reg(ci, 0x06, 0x1F); + if (status < 0) + break; + status = write_reg(ci, 0x07, 0x1F); + if (status < 0) + break; + status = write_reg(ci, 0x08, 0x28); + if (status < 0) + break; + status = write_reg(ci, 0x14, 0x20); + if (status < 0) + break; + +#if 0 + status = write_reg(ci, 0x09, 0x4D); /* Input Mode C, BYPass Serial, TIVAL = low, MSB */ + if (status < 0) + break; +#endif + status = write_reg(ci, 0x0A, 0xA7); /* TOSTRT = 8, Mode B (gated clock), falling Edge, Serial, POL=HIGH, MSB */ + if (status < 0) + break; + + status = write_reg(ci, 0x0B, 0x33); + if (status < 0) + break; + status = write_reg(ci, 0x0C, 0x33); + if (status < 0) + break; + + status = write_regm(ci, 0x14, 0x00, 0x0F); + if (status < 0) + break; + status = write_reg(ci, 0x15, ci->clk_reg_b); + if (status < 0) + break; + status = write_regm(ci, 0x16, 0x00, 0x0F); + if (status < 0) + break; + status = write_reg(ci, 0x17, ci->clk_reg_f); + if (status < 0) + break; + + if (ci->cfg.clock_mode) { + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x6f); + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x6d); + if (status < 0) + break; + } + status = write_reg(ci, 0x20, 0x68); + if (status < 0) + break; + status = write_reg(ci, 0x21, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x22, 0x02); + if (status < 0) + break; + } else { + if (ci->cfg.polarity) { + status = write_reg(ci, 0x09, 0x4f); + if (status < 0) + break; + } else { + status = write_reg(ci, 0x09, 0x4d); + if (status < 0) + break; + } + + status = write_reg(ci, 0x20, 0x28); + if (status < 0) + break; + status = write_reg(ci, 0x21, 0x00); + if (status < 0) + break; + status = write_reg(ci, 0x22, 0x07); + if (status < 0) + break; + } + + status = write_regm(ci, 0x20, 0x80, 0x80); + if (status < 0) + break; + status = write_regm(ci, 0x03, 0x02, 0x02); + if (status < 0) + break; + status = write_reg(ci, 0x01, 0x04); + if (status < 0) + break; + status = write_reg(ci, 0x00, 0x31); + if (status < 0) + break; + + /* Put TS in bypass */ + status = write_regm(ci, 0x09, 0x08, 0x08); + if (status < 0) + break; + ci->cammode = -1; + cam_mode(ci, 0); + } while (0); + mutex_unlock(&ci->lock); + + return 0; +} + +static int read_attribute_mem(struct dvb_ca_en50221 *ca, + int slot, int address) +{ + struct cxd *ci = ca->data; +#if 0 + if (ci->amem_read) { + if (address <= 0 || address > 1024) + return -EIO; + return ci->amem[address]; + } + + mutex_lock(&ci->lock); + write_regm(ci, 0x06, 0x00, 0x05); + read_pccard(ci, 0, &ci->amem[0], 128); + read_pccard(ci, 128, &ci->amem[0], 128); + read_pccard(ci, 256, &ci->amem[0], 128); + read_pccard(ci, 384, &ci->amem[0], 128); + write_regm(ci, 0x06, 0x05, 0x05); + mutex_unlock(&ci->lock); + return ci->amem[address]; +#else + u8 val; + mutex_lock(&ci->lock); + set_mode(ci, 1); + read_pccard(ci, address, &val, 1); + mutex_unlock(&ci->lock); + /* printk(KERN_INFO "%02x:%02x\n", address,val); */ + return val; +#endif +} + +static int write_attribute_mem(struct dvb_ca_en50221 *ca, int slot, + int address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 1); + write_pccard(ci, address, &value, 1); + mutex_unlock(&ci->lock); + return 0; +} + +static int read_cam_control(struct dvb_ca_en50221 *ca, + int slot, u8 address) +{ + struct cxd *ci = ca->data; + u8 val; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + read_io(ci, address, &val); + mutex_unlock(&ci->lock); + return val; +} + +static int write_cam_control(struct dvb_ca_en50221 *ca, int slot, + u8 address, u8 value) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + set_mode(ci, 0); + write_io(ci, address, value); + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_reset(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); +#if 0 + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x00, 0x31); +#else +#if 0 + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x06, 0x2F); +#else + cam_mode(ci, 0); + write_reg(ci, 0x00, 0x21); + write_reg(ci, 0x06, 0x1F); + write_reg(ci, 0x00, 0x31); + write_regm(ci, 0x20, 0x80, 0x80); + write_reg(ci, 0x03, 0x02); + ci->ready = 0; +#endif +#endif + ci->mode = -1; + { + int i; +#if 0 + u8 val; +#endif + for (i = 0; i < 100; i++) { + msleep(10); +#if 0 + read_reg(ci, 0x06, &val); + printk(KERN_INFO "%d:%02x\n", i, val); + if (!(val&0x10)) + break; +#else + if (ci->ready) + break; +#endif + } + } + mutex_unlock(&ci->lock); + /* msleep(500); */ + return 0; +} + +static int slot_shutdown(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + printk(KERN_INFO "slot_shutdown\n"); + mutex_lock(&ci->lock); + write_regm(ci, 0x09, 0x08, 0x08); + write_regm(ci, 0x20, 0x80, 0x80); /* Reset CAM Mode */ + write_regm(ci, 0x06, 0x07, 0x07); /* Clear IO Mode */ + ci->mode = -1; + mutex_unlock(&ci->lock); + return 0; +} + +static int slot_ts_enable(struct dvb_ca_en50221 *ca, int slot) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + write_regm(ci, 0x09, 0x00, 0x08); + set_mode(ci, 0); +#ifdef BUFFER_MODE + cam_mode(ci, 1); +#endif + mutex_unlock(&ci->lock); + return 0; +} + + +static int campoll(struct cxd *ci) +{ + u8 istat; + + read_reg(ci, 0x04, &istat); + if (!istat) + return 0; + write_reg(ci, 0x05, istat); + + if (istat&0x40) { + ci->dr = 1; + printk(KERN_INFO "DR\n"); + } + if (istat&0x20) + printk(KERN_INFO "WC\n"); + + if (istat&2) { + u8 slotstat; + + read_reg(ci, 0x01, &slotstat); + if (!(2&slotstat)) { + if (!ci->slot_stat) { + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_PRESENT; + write_regm(ci, 0x03, 0x08, 0x08); + } + + } else { + if (ci->slot_stat) { + ci->slot_stat = 0; + write_regm(ci, 0x03, 0x00, 0x08); + printk(KERN_INFO "NO CAM\n"); + ci->ready = 0; + } + } + if (istat&8 && ci->slot_stat == DVB_CA_EN50221_POLL_CAM_PRESENT) { + ci->ready = 1; + ci->slot_stat |= DVB_CA_EN50221_POLL_CAM_READY; + } + } + return 0; +} + + +static int poll_slot_status(struct dvb_ca_en50221 *ca, int slot, int open) +{ + struct cxd *ci = ca->data; + u8 slotstat; + + mutex_lock(&ci->lock); + campoll(ci); + read_reg(ci, 0x01, &slotstat); + mutex_unlock(&ci->lock); + + return ci->slot_stat; +} + +#ifdef BUFFER_MODE +static int read_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + u8 msb, lsb; + u16 len; + + mutex_lock(&ci->lock); + campoll(ci); + mutex_unlock(&ci->lock); + + printk(KERN_INFO "read_data\n"); + if (!ci->dr) + return 0; + + mutex_lock(&ci->lock); + read_reg(ci, 0x0f, &msb); + read_reg(ci, 0x10, &lsb); + len = (msb<<8)|lsb; + read_block(ci, 0x12, ebuf, len); + ci->dr = 0; + mutex_unlock(&ci->lock); + + return len; +} + +static int write_data(struct dvb_ca_en50221 *ca, int slot, u8 *ebuf, int ecount) +{ + struct cxd *ci = ca->data; + + mutex_lock(&ci->lock); + printk(kern_INFO "write_data %d\n", ecount); + write_reg(ci, 0x0d, ecount>>8); + write_reg(ci, 0x0e, ecount&0xff); + write_block(ci, 0x11, ebuf, ecount); + mutex_unlock(&ci->lock); + return ecount; +} +#endif + +static struct dvb_ca_en50221 en_templ = { + .read_attribute_mem = read_attribute_mem, + .write_attribute_mem = write_attribute_mem, + .read_cam_control = read_cam_control, + .write_cam_control = write_cam_control, + .slot_reset = slot_reset, + .slot_shutdown = slot_shutdown, + .slot_ts_enable = slot_ts_enable, + .poll_slot_status = poll_slot_status, +#ifdef BUFFER_MODE + .read_data = read_data, + .write_data = write_data, +#endif + +}; + +struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, + void *priv, + struct i2c_adapter *i2c) +{ + struct cxd *ci = 0; + u8 val; + + if (i2c_read_reg(i2c, cfg->adr, 0, &val) < 0) { + printk(KERN_INFO "No CXD2099 detected at %02x\n", cfg->adr); + return 0; + } + + ci = kmalloc(sizeof(struct cxd), GFP_KERNEL); + if (!ci) + return 0; + memset(ci, 0, sizeof(*ci)); + + mutex_init(&ci->lock); + memcpy(&ci->cfg, cfg, sizeof(struct cxd2099_cfg)); + ci->i2c = i2c; + ci->lastaddress = 0xff; + ci->clk_reg_b = 0x4a; + ci->clk_reg_f = 0x1b; + + memcpy(&ci->en, &en_templ, sizeof(en_templ)); + ci->en.data = ci; + init(ci); + printk(KERN_INFO "Attached CXD2099AR at %02x\n", ci->cfg.adr); + return &ci->en; +} +EXPORT_SYMBOL(cxd2099_attach); + +MODULE_DESCRIPTION("cxd2099"); +MODULE_AUTHOR("Ralph Metzler"); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/cxd2099/cxd2099.h b/drivers/staging/media/cxd2099/cxd2099.h new file mode 100644 index 000000000000..19c588a59588 --- /dev/null +++ b/drivers/staging/media/cxd2099/cxd2099.h @@ -0,0 +1,51 @@ +/* + * cxd2099.h: Driver for the CXD2099AR Common Interface Controller + * + * 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 _CXD2099_H_ +#define _CXD2099_H_ + +#include + +struct cxd2099_cfg { + u32 bitrate; + u8 adr; + u8 polarity:1; + u8 clock_mode:1; +}; + +#if defined(CONFIG_DVB_CXD2099) || \ + (defined(CONFIG_DVB_CXD2099_MODULE) && defined(MODULE)) +struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, + void *priv, struct i2c_adapter *i2c); +#else + +static inline struct dvb_ca_en50221 *cxd2099_attach(struct cxd2099_cfg *cfg, + void *priv, struct i2c_adapter *i2c) +{ + printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__); + return NULL; +} +#endif + +#endif diff --git a/drivers/staging/media/dt3155v4l/Kconfig b/drivers/staging/media/dt3155v4l/Kconfig new file mode 100644 index 000000000000..226a1ca90b3c --- /dev/null +++ b/drivers/staging/media/dt3155v4l/Kconfig @@ -0,0 +1,28 @@ +config VIDEO_DT3155 + tristate "DT3155 frame grabber, Video4Linux interface" + depends on PCI && VIDEO_DEV && VIDEO_V4L2 + select VIDEOBUF2_DMA_CONTIG + default n + ---help--- + Enables dt3155 device driver for the DataTranslation DT3155 frame grabber. + Say Y here if you have this hardware. + In doubt, say N. + + To compile this driver as a module, choose M here: the + module will be called dt3155v4l. + +config DT3155_CCIR + bool "Selects CCIR/50Hz vertical refresh" + depends on VIDEO_DT3155 + default y + ---help--- + Select it for CCIR/50Hz (European region), + or leave it unselected for RS-170/60Hz (North America). + +config DT3155_STREAMING + bool "Selects streaming capture method" + depends on VIDEO_DT3155 + default y + ---help--- + Select it if you want to use streaming of memory mapped buffers + or leave it unselected if you want to use read method (one copy more). diff --git a/drivers/staging/media/dt3155v4l/Makefile b/drivers/staging/media/dt3155v4l/Makefile new file mode 100644 index 000000000000..ce7a3ec2faf3 --- /dev/null +++ b/drivers/staging/media/dt3155v4l/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_VIDEO_DT3155) += dt3155v4l.o diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.c b/drivers/staging/media/dt3155v4l/dt3155v4l.c new file mode 100644 index 000000000000..04e93c49f03a --- /dev/null +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.c @@ -0,0 +1,993 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Marin Mitov * + * mitov@issp.bas.bg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public 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 "dt3155v4l.h" + +#define DT3155_VENDOR_ID 0x8086 +#define DT3155_DEVICE_ID 0x1223 + +/* DT3155_CHUNK_SIZE is 4M (2^22) 8 full size buffers */ +#define DT3155_CHUNK_SIZE (1U << 22) + +#define DT3155_COH_FLAGS (GFP_KERNEL | GFP_DMA32 | __GFP_COLD | __GFP_NOWARN) + +#define DT3155_BUF_SIZE (768 * 576) + +#ifdef CONFIG_DT3155_STREAMING +#define DT3155_CAPTURE_METHOD V4L2_CAP_STREAMING +#else +#define DT3155_CAPTURE_METHOD V4L2_CAP_READWRITE +#endif + +/* global initializers (for all boards) */ +#ifdef CONFIG_DT3155_CCIR +static const u8 csr2_init = VT_50HZ; +#define DT3155_CURRENT_NORM V4L2_STD_625_50 +static const unsigned int img_width = 768; +static const unsigned int img_height = 576; +static const unsigned int frames_per_sec = 25; +static const struct v4l2_fmtdesc frame_std[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "CCIR/50Hz 8 bits gray", + .pixelformat = V4L2_PIX_FMT_GREY, + }, +}; +#else +static const u8 csr2_init = VT_60HZ; +#define DT3155_CURRENT_NORM V4L2_STD_525_60 +static const unsigned int img_width = 640; +static const unsigned int img_height = 480; +static const unsigned int frames_per_sec = 30; +static const struct v4l2_fmtdesc frame_std[] = { + { + .index = 0, + .type = V4L2_BUF_TYPE_VIDEO_CAPTURE, + .flags = 0, + .description = "RS-170/60Hz 8 bits gray", + .pixelformat = V4L2_PIX_FMT_GREY, + }, +}; +#endif + +#define NUM_OF_FORMATS ARRAY_SIZE(frame_std) + +static u8 config_init = ACQ_MODE_EVEN; + +/** + * read_i2c_reg - reads an internal i2c register + * + * @addr: dt3155 mmio base address + * @index: index (internal address) of register to read + * @data: pointer to byte the read data will be placed in + * + * returns: zero on success or error code + * + * This function starts reading the specified (by index) register + * and busy waits for the process to finish. The result is placed + * in a byte pointed by data. + */ +static int +read_i2c_reg(void __iomem *addr, u8 index, u8 *data) +{ + u32 tmp = index; + + iowrite32((tmp<<17) | IIC_READ, addr + IIC_CSR2); + mmiowb(); + udelay(45); /* wait at least 43 usec for NEW_CYCLE to clear */ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + return -EIO; /* error: NEW_CYCLE not cleared */ + tmp = ioread32(addr + IIC_CSR1); + if (tmp & DIRECT_ABORT) { + /* reset DIRECT_ABORT bit */ + iowrite32(DIRECT_ABORT, addr + IIC_CSR1); + return -EIO; /* error: DIRECT_ABORT set */ + } + *data = tmp>>24; + return 0; +} + +/** + * write_i2c_reg - writes to an internal i2c register + * + * @addr: dt3155 mmio base address + * @index: index (internal address) of register to read + * @data: data to be written + * + * returns: zero on success or error code + * + * This function starts writting the specified (by index) register + * and busy waits for the process to finish. + */ +static int +write_i2c_reg(void __iomem *addr, u8 index, u8 data) +{ + u32 tmp = index; + + iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2); + mmiowb(); + udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + return -EIO; /* error: NEW_CYCLE not cleared */ + if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { + /* reset DIRECT_ABORT bit */ + iowrite32(DIRECT_ABORT, addr + IIC_CSR1); + return -EIO; /* error: DIRECT_ABORT set */ + } + return 0; +} + +/** + * write_i2c_reg_nowait - writes to an internal i2c register + * + * @addr: dt3155 mmio base address + * @index: index (internal address) of register to read + * @data: data to be written + * + * This function starts writting the specified (by index) register + * and then returns. + */ +static void write_i2c_reg_nowait(void __iomem *addr, u8 index, u8 data) +{ + u32 tmp = index; + + iowrite32((tmp<<17) | IIC_WRITE | data, addr + IIC_CSR2); + mmiowb(); +} + +/** + * wait_i2c_reg - waits the read/write to finish + * + * @addr: dt3155 mmio base address + * + * returns: zero on success or error code + * + * This function waits reading/writting to finish. + */ +static int wait_i2c_reg(void __iomem *addr) +{ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + udelay(65); /* wait at least 63 usec for NEW_CYCLE to clear */ + if (ioread32(addr + IIC_CSR2) & NEW_CYCLE) + return -EIO; /* error: NEW_CYCLE not cleared */ + if (ioread32(addr + IIC_CSR1) & DIRECT_ABORT) { + /* reset DIRECT_ABORT bit */ + iowrite32(DIRECT_ABORT, addr + IIC_CSR1); + return -EIO; /* error: DIRECT_ABORT set */ + } + return 0; +} + +static int +dt3155_start_acq(struct dt3155_priv *pd) +{ + struct vb2_buffer *vb = pd->curr_buf; + dma_addr_t dma_addr; + + dma_addr = vb2_dma_contig_plane_dma_addr(vb, 0); + iowrite32(dma_addr, pd->regs + EVEN_DMA_START); + iowrite32(dma_addr + img_width, pd->regs + ODD_DMA_START); + iowrite32(img_width, pd->regs + EVEN_DMA_STRIDE); + iowrite32(img_width, pd->regs + ODD_DMA_STRIDE); + /* enable interrupts, clear all irq flags */ + iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | + FLD_END_EVEN | FLD_END_ODD, pd->regs + INT_CSR); + iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | + FLD_DN_ODD | FLD_DN_EVEN | CAP_CONT_EVEN | CAP_CONT_ODD, + pd->regs + CSR1); + wait_i2c_reg(pd->regs); + write_i2c_reg(pd->regs, CONFIG, pd->config); + write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); + write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); + + /* start the board */ + write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | BUSY_ODD); + return 0; /* success */ +} + +/* + * driver-specific callbacks (vb2_ops) + */ +static int +dt3155_queue_setup(struct vb2_queue *q, unsigned int *num_buffers, + unsigned int *num_planes, unsigned long sizes[], + void *alloc_ctxs[]) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(q); + void *ret; + + if (*num_buffers == 0) + *num_buffers = 1; + *num_planes = 1; + sizes[0] = img_width * img_height; + if (pd->q->alloc_ctx[0]) + return 0; + ret = vb2_dma_contig_init_ctx(&pd->pdev->dev); + if (IS_ERR(ret)) + return PTR_ERR(ret); + pd->q->alloc_ctx[0] = ret; + return 0; +} + +static void +dt3155_wait_prepare(struct vb2_queue *q) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(q); + + mutex_unlock(pd->vdev->lock); +} + +static void +dt3155_wait_finish(struct vb2_queue *q) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(q); + + mutex_lock(pd->vdev->lock); +} + +static int +dt3155_buf_prepare(struct vb2_buffer *vb) +{ + vb2_set_plane_payload(vb, 0, img_width * img_height); + return 0; +} + +static int +dt3155_start_streaming(struct vb2_queue *q) +{ + return 0; +} + +static int +dt3155_stop_streaming(struct vb2_queue *q) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(q); + struct vb2_buffer *vb; + + spin_lock_irq(&pd->lock); + while (!list_empty(&pd->dmaq)) { + vb = list_first_entry(&pd->dmaq, typeof(*vb), done_entry); + list_del(&vb->done_entry); + vb2_buffer_done(vb, VB2_BUF_STATE_ERROR); + } + spin_unlock_irq(&pd->lock); + msleep(45); /* irq hendler will stop the hardware */ + return 0; +} + +static void +dt3155_buf_queue(struct vb2_buffer *vb) +{ + struct dt3155_priv *pd = vb2_get_drv_priv(vb->vb2_queue); + + /* pd->q->streaming = 1 when dt3155_buf_queue() is invoked */ + spin_lock_irq(&pd->lock); + if (pd->curr_buf) + list_add_tail(&vb->done_entry, &pd->dmaq); + else { + pd->curr_buf = vb; + dt3155_start_acq(pd); + } + spin_unlock_irq(&pd->lock); +} +/* + * end driver-specific callbacks + */ + +const struct vb2_ops q_ops = { + .queue_setup = dt3155_queue_setup, + .wait_prepare = dt3155_wait_prepare, + .wait_finish = dt3155_wait_finish, + .buf_prepare = dt3155_buf_prepare, + .start_streaming = dt3155_start_streaming, + .stop_streaming = dt3155_stop_streaming, + .buf_queue = dt3155_buf_queue, +}; + +static irqreturn_t +dt3155_irq_handler_even(int irq, void *dev_id) +{ + struct dt3155_priv *ipd = dev_id; + struct vb2_buffer *ivb; + dma_addr_t dma_addr; + u32 tmp; + + tmp = ioread32(ipd->regs + INT_CSR) & (FLD_START | FLD_END_ODD); + if (!tmp) + return IRQ_NONE; /* not our irq */ + if ((tmp & FLD_START) && !(tmp & FLD_END_ODD)) { + iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START, + ipd->regs + INT_CSR); + ipd->field_count++; + return IRQ_HANDLED; /* start of field irq */ + } + if ((tmp & FLD_START) && (tmp & FLD_END_ODD)) + ipd->stats.start_before_end++; + /* check for corrupted fields */ +/* write_i2c_reg(ipd->regs, EVEN_CSR, CSR_ERROR | CSR_DONE); */ +/* write_i2c_reg(ipd->regs, ODD_CSR, CSR_ERROR | CSR_DONE); */ + tmp = ioread32(ipd->regs + CSR1) & (FLD_CRPT_EVEN | FLD_CRPT_ODD); + if (tmp) { + ipd->stats.corrupted_fields++; + iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | + FLD_DN_ODD | FLD_DN_EVEN | + CAP_CONT_EVEN | CAP_CONT_ODD, + ipd->regs + CSR1); + mmiowb(); + } + + spin_lock(&ipd->lock); + if (ipd->curr_buf) { + do_gettimeofday(&ipd->curr_buf->v4l2_buf.timestamp); + ipd->curr_buf->v4l2_buf.sequence = (ipd->field_count) >> 1; + vb2_buffer_done(ipd->curr_buf, VB2_BUF_STATE_DONE); + } + + if (!ipd->q->streaming || list_empty(&ipd->dmaq)) + goto stop_dma; + ivb = list_first_entry(&ipd->dmaq, typeof(*ivb), done_entry); + list_del(&ivb->done_entry); + ipd->curr_buf = ivb; + dma_addr = vb2_dma_contig_plane_dma_addr(ivb, 0); + iowrite32(dma_addr, ipd->regs + EVEN_DMA_START); + iowrite32(dma_addr + img_width, ipd->regs + ODD_DMA_START); + iowrite32(img_width, ipd->regs + EVEN_DMA_STRIDE); + iowrite32(img_width, ipd->regs + ODD_DMA_STRIDE); + mmiowb(); + /* enable interrupts, clear all irq flags */ + iowrite32(FLD_START_EN | FLD_END_ODD_EN | FLD_START | + FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR); + spin_unlock(&ipd->lock); + return IRQ_HANDLED; + +stop_dma: + ipd->curr_buf = NULL; + /* stop the board */ + write_i2c_reg_nowait(ipd->regs, CSR2, ipd->csr2); + iowrite32(FIFO_EN | SRST | FLD_CRPT_ODD | FLD_CRPT_EVEN | + FLD_DN_ODD | FLD_DN_EVEN, ipd->regs + CSR1); + /* disable interrupts, clear all irq flags */ + iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, ipd->regs + INT_CSR); + spin_unlock(&ipd->lock); + return IRQ_HANDLED; +} + +static int +dt3155_open(struct file *filp) +{ + int ret = 0; + struct dt3155_priv *pd = video_drvdata(filp); + + if (!pd->users) { + pd->q = kzalloc(sizeof(*pd->q), GFP_KERNEL); + if (!pd->q) { + ret = -ENOMEM; + goto err_alloc_queue; + } + pd->q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + pd->q->io_modes = VB2_READ | VB2_MMAP; + pd->q->ops = &q_ops; + pd->q->mem_ops = &vb2_dma_contig_memops; + pd->q->drv_priv = pd; + pd->curr_buf = NULL; + pd->field_count = 0; + vb2_queue_init(pd->q); /* cannot fail */ + INIT_LIST_HEAD(&pd->dmaq); + spin_lock_init(&pd->lock); + /* disable all irqs, clear all irq flags */ + iowrite32(FLD_START | FLD_END_EVEN | FLD_END_ODD, + pd->regs + INT_CSR); + ret = request_irq(pd->pdev->irq, dt3155_irq_handler_even, + IRQF_SHARED, DT3155_NAME, pd); + if (ret) + goto err_request_irq; + } + pd->users++; + return 0; /* success */ +err_request_irq: + kfree(pd->q); + pd->q = NULL; +err_alloc_queue: + return ret; +} + +static int +dt3155_release(struct file *filp) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + pd->users--; + BUG_ON(pd->users < 0); + if (!pd->users) { + vb2_queue_release(pd->q); + free_irq(pd->pdev->irq, pd); + if (pd->q->alloc_ctx[0]) + vb2_dma_contig_cleanup_ctx(pd->q->alloc_ctx[0]); + kfree(pd->q); + pd->q = NULL; + } + return 0; +} + +static ssize_t +dt3155_read(struct file *filp, char __user *user, size_t size, loff_t *loff) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_read(pd->q, user, size, loff, filp->f_flags & O_NONBLOCK); +} + +static unsigned int +dt3155_poll(struct file *filp, struct poll_table_struct *polltbl) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_poll(pd->q, filp, polltbl); +} + +static int +dt3155_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_mmap(pd->q, vma); +} + +static const struct v4l2_file_operations dt3155_fops = { + .owner = THIS_MODULE, + .open = dt3155_open, + .release = dt3155_release, + .read = dt3155_read, + .poll = dt3155_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = dt3155_mmap, +}; + +static int +dt3155_ioc_streamon(struct file *filp, void *p, enum v4l2_buf_type type) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_streamon(pd->q, type); +} + +static int +dt3155_ioc_streamoff(struct file *filp, void *p, enum v4l2_buf_type type) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_streamoff(pd->q, type); +} + +static int +dt3155_ioc_querycap(struct file *filp, void *p, struct v4l2_capability *cap) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + strcpy(cap->driver, DT3155_NAME); + strcpy(cap->card, DT3155_NAME " frame grabber"); + sprintf(cap->bus_info, "PCI:%s", pci_name(pd->pdev)); + cap->version = + KERNEL_VERSION(DT3155_VER_MAJ, DT3155_VER_MIN, DT3155_VER_EXT); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + DT3155_CAPTURE_METHOD; + return 0; +} + +static int +dt3155_ioc_enum_fmt_vid_cap(struct file *filp, void *p, struct v4l2_fmtdesc *f) +{ + if (f->index >= NUM_OF_FORMATS) + return -EINVAL; + *f = frame_std[f->index]; + return 0; +} + +static int +dt3155_ioc_g_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + f->fmt.pix.width = img_width; + f->fmt.pix.height = img_height; + f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = f->fmt.pix.width; + f->fmt.pix.sizeimage = f->fmt.pix.width * f->fmt.pix.height; + f->fmt.pix.colorspace = 0; + f->fmt.pix.priv = 0; + return 0; +} + +static int +dt3155_ioc_try_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) +{ + if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (f->fmt.pix.width == img_width && + f->fmt.pix.height == img_height && + f->fmt.pix.pixelformat == V4L2_PIX_FMT_GREY && + f->fmt.pix.field == V4L2_FIELD_NONE && + f->fmt.pix.bytesperline == f->fmt.pix.width && + f->fmt.pix.sizeimage == f->fmt.pix.width * f->fmt.pix.height) + return 0; + else + return -EINVAL; +} + +static int +dt3155_ioc_s_fmt_vid_cap(struct file *filp, void *p, struct v4l2_format *f) +{ + return dt3155_ioc_g_fmt_vid_cap(filp, p, f); +} + +static int +dt3155_ioc_reqbufs(struct file *filp, void *p, struct v4l2_requestbuffers *b) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_reqbufs(pd->q, b); +} + +static int +dt3155_ioc_querybuf(struct file *filp, void *p, struct v4l2_buffer *b) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_querybuf(pd->q, b); +} + +static int +dt3155_ioc_qbuf(struct file *filp, void *p, struct v4l2_buffer *b) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_qbuf(pd->q, b); +} + +static int +dt3155_ioc_dqbuf(struct file *filp, void *p, struct v4l2_buffer *b) +{ + struct dt3155_priv *pd = video_drvdata(filp); + + return vb2_dqbuf(pd->q, b, filp->f_flags & O_NONBLOCK); +} + +static int +dt3155_ioc_querystd(struct file *filp, void *p, v4l2_std_id *norm) +{ + *norm = DT3155_CURRENT_NORM; + return 0; +} + +static int +dt3155_ioc_g_std(struct file *filp, void *p, v4l2_std_id *norm) +{ + *norm = DT3155_CURRENT_NORM; + return 0; +} + +static int +dt3155_ioc_s_std(struct file *filp, void *p, v4l2_std_id *norm) +{ + if (*norm & DT3155_CURRENT_NORM) + return 0; + return -EINVAL; +} + +static int +dt3155_ioc_enum_input(struct file *filp, void *p, struct v4l2_input *input) +{ + if (input->index) + return -EINVAL; + strcpy(input->name, "Coax in"); + input->type = V4L2_INPUT_TYPE_CAMERA; + /* + * FIXME: input->std = 0 according to v4l2 API + * VIDIOC_G_STD, VIDIOC_S_STD, VIDIOC_QUERYSTD and VIDIOC_ENUMSTD + * should return -EINVAL + */ + input->std = DT3155_CURRENT_NORM; + input->status = 0;/* FIXME: add sync detection & V4L2_IN_ST_NO_H_LOCK */ + return 0; +} + +static int +dt3155_ioc_g_input(struct file *filp, void *p, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int +dt3155_ioc_s_input(struct file *filp, void *p, unsigned int i) +{ + if (i) + return -EINVAL; + return 0; +} + +static int +dt3155_ioc_g_parm(struct file *filp, void *p, struct v4l2_streamparm *parms) +{ + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parms->parm.capture.capturemode = 0; + parms->parm.capture.timeperframe.numerator = 1001; + parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000; + parms->parm.capture.extendedmode = 0; + parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */ + return 0; +} + +static int +dt3155_ioc_s_parm(struct file *filp, void *p, struct v4l2_streamparm *parms) +{ + if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + parms->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + parms->parm.capture.capturemode = 0; + parms->parm.capture.timeperframe.numerator = 1001; + parms->parm.capture.timeperframe.denominator = frames_per_sec * 1000; + parms->parm.capture.extendedmode = 0; + parms->parm.capture.readbuffers = 1; /* FIXME: 2 buffers? */ + return 0; +} + +static const struct v4l2_ioctl_ops dt3155_ioctl_ops = { + .vidioc_streamon = dt3155_ioc_streamon, + .vidioc_streamoff = dt3155_ioc_streamoff, + .vidioc_querycap = dt3155_ioc_querycap, +/* + .vidioc_g_priority = dt3155_ioc_g_priority, + .vidioc_s_priority = dt3155_ioc_s_priority, +*/ + .vidioc_enum_fmt_vid_cap = dt3155_ioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = dt3155_ioc_try_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = dt3155_ioc_g_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = dt3155_ioc_s_fmt_vid_cap, + .vidioc_reqbufs = dt3155_ioc_reqbufs, + .vidioc_querybuf = dt3155_ioc_querybuf, + .vidioc_qbuf = dt3155_ioc_qbuf, + .vidioc_dqbuf = dt3155_ioc_dqbuf, + .vidioc_querystd = dt3155_ioc_querystd, + .vidioc_g_std = dt3155_ioc_g_std, + .vidioc_s_std = dt3155_ioc_s_std, + .vidioc_enum_input = dt3155_ioc_enum_input, + .vidioc_g_input = dt3155_ioc_g_input, + .vidioc_s_input = dt3155_ioc_s_input, +/* + .vidioc_queryctrl = dt3155_ioc_queryctrl, + .vidioc_g_ctrl = dt3155_ioc_g_ctrl, + .vidioc_s_ctrl = dt3155_ioc_s_ctrl, + .vidioc_querymenu = dt3155_ioc_querymenu, + .vidioc_g_ext_ctrls = dt3155_ioc_g_ext_ctrls, + .vidioc_s_ext_ctrls = dt3155_ioc_s_ext_ctrls, +*/ + .vidioc_g_parm = dt3155_ioc_g_parm, + .vidioc_s_parm = dt3155_ioc_s_parm, +/* + .vidioc_cropcap = dt3155_ioc_cropcap, + .vidioc_g_crop = dt3155_ioc_g_crop, + .vidioc_s_crop = dt3155_ioc_s_crop, + .vidioc_enum_framesizes = dt3155_ioc_enum_framesizes, + .vidioc_enum_frameintervals = dt3155_ioc_enum_frameintervals, +*/ +}; + +static int __devinit +dt3155_init_board(struct pci_dev *pdev) +{ + struct dt3155_priv *pd = pci_get_drvdata(pdev); + void *buf_cpu; + dma_addr_t buf_dma; + int i; + u8 tmp; + + pci_set_master(pdev); /* dt3155 needs it */ + + /* resetting the adapter */ + iowrite32(FLD_CRPT_ODD | FLD_CRPT_EVEN | FLD_DN_ODD | FLD_DN_EVEN, + pd->regs + CSR1); + mmiowb(); + msleep(20); + + /* initializing adaper registers */ + iowrite32(FIFO_EN | SRST, pd->regs + CSR1); + mmiowb(); + iowrite32(0xEEEEEE01, pd->regs + EVEN_PIXEL_FMT); + iowrite32(0xEEEEEE01, pd->regs + ODD_PIXEL_FMT); + iowrite32(0x00000020, pd->regs + FIFO_TRIGER); + iowrite32(0x00000103, pd->regs + XFER_MODE); + iowrite32(0, pd->regs + RETRY_WAIT_CNT); + iowrite32(0, pd->regs + INT_CSR); + iowrite32(1, pd->regs + EVEN_FLD_MASK); + iowrite32(1, pd->regs + ODD_FLD_MASK); + iowrite32(0, pd->regs + MASK_LENGTH); + iowrite32(0x0005007C, pd->regs + FIFO_FLAG_CNT); + iowrite32(0x01010101, pd->regs + IIC_CLK_DUR); + mmiowb(); + + /* verifying that we have a DT3155 board (not just a SAA7116 chip) */ + read_i2c_reg(pd->regs, DT_ID, &tmp); + if (tmp != DT3155_ID) + return -ENODEV; + + /* initialize AD LUT */ + write_i2c_reg(pd->regs, AD_ADDR, 0); + for (i = 0; i < 256; i++) + write_i2c_reg(pd->regs, AD_LUT, i); + + /* initialize ADC references */ + /* FIXME: pos_ref & neg_ref depend on VT_50HZ */ + write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); + write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); + write_i2c_reg(pd->regs, AD_ADDR, AD_POS_REF); + write_i2c_reg(pd->regs, AD_CMD, 34); + write_i2c_reg(pd->regs, AD_ADDR, AD_NEG_REF); + write_i2c_reg(pd->regs, AD_CMD, 0); + + /* initialize PM LUT */ + write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM); + for (i = 0; i < 256; i++) { + write_i2c_reg(pd->regs, PM_LUT_ADDR, i); + write_i2c_reg(pd->regs, PM_LUT_DATA, i); + } + write_i2c_reg(pd->regs, CONFIG, pd->config | PM_LUT_PGM | PM_LUT_SEL); + for (i = 0; i < 256; i++) { + write_i2c_reg(pd->regs, PM_LUT_ADDR, i); + write_i2c_reg(pd->regs, PM_LUT_DATA, i); + } + write_i2c_reg(pd->regs, CONFIG, pd->config); /* ACQ_MODE_EVEN */ + + /* select chanel 1 for input and set sync level */ + write_i2c_reg(pd->regs, AD_ADDR, AD_CMD_REG); + write_i2c_reg(pd->regs, AD_CMD, VIDEO_CNL_1 | SYNC_CNL_1 | SYNC_LVL_3); + + /* allocate memory, and initialize the DMA machine */ + buf_cpu = dma_alloc_coherent(&pdev->dev, DT3155_BUF_SIZE, &buf_dma, + GFP_KERNEL); + if (!buf_cpu) + return -ENOMEM; + iowrite32(buf_dma, pd->regs + EVEN_DMA_START); + iowrite32(buf_dma, pd->regs + ODD_DMA_START); + iowrite32(0, pd->regs + EVEN_DMA_STRIDE); + iowrite32(0, pd->regs + ODD_DMA_STRIDE); + + /* Perform a pseudo even field acquire */ + iowrite32(FIFO_EN | SRST | CAP_CONT_ODD, pd->regs + CSR1); + write_i2c_reg(pd->regs, CSR2, pd->csr2 | SYNC_SNTL); + write_i2c_reg(pd->regs, CONFIG, pd->config); + write_i2c_reg(pd->regs, EVEN_CSR, CSR_SNGL); + write_i2c_reg(pd->regs, CSR2, pd->csr2 | BUSY_EVEN | SYNC_SNTL); + msleep(100); + read_i2c_reg(pd->regs, CSR2, &tmp); + write_i2c_reg(pd->regs, EVEN_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE); + write_i2c_reg(pd->regs, ODD_CSR, CSR_ERROR | CSR_SNGL | CSR_DONE); + write_i2c_reg(pd->regs, CSR2, pd->csr2); + iowrite32(FIFO_EN | SRST | FLD_DN_EVEN | FLD_DN_ODD, pd->regs + CSR1); + + /* deallocate memory */ + dma_free_coherent(&pdev->dev, DT3155_BUF_SIZE, buf_cpu, buf_dma); + if (tmp & BUSY_EVEN) + return -EIO; + return 0; +} + +static struct video_device dt3155_vdev = { + .name = DT3155_NAME, + .fops = &dt3155_fops, + .ioctl_ops = &dt3155_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = DT3155_CURRENT_NORM, + .current_norm = DT3155_CURRENT_NORM, +}; + +/* same as in drivers/base/dma-coherent.c */ +struct dma_coherent_mem { + void *virt_base; + dma_addr_t device_base; + int size; + int flags; + unsigned long *bitmap; +}; + +static int __devinit +dt3155_alloc_coherent(struct device *dev, size_t size, int flags) +{ + struct dma_coherent_mem *mem; + dma_addr_t dev_base; + int pages = size >> PAGE_SHIFT; + int bitmap_size = BITS_TO_LONGS(pages) * sizeof(long); + + if ((flags & DMA_MEMORY_MAP) == 0) + goto out; + if (!size) + goto out; + if (dev->dma_mem) + goto out; + + mem = kzalloc(sizeof(*mem), GFP_KERNEL); + if (!mem) + goto out; + mem->virt_base = dma_alloc_coherent(dev, size, &dev_base, + DT3155_COH_FLAGS); + if (!mem->virt_base) + goto err_alloc_coherent; + mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); + if (!mem->bitmap) + goto err_bitmap; + + /* coherent_dma_mask is already set to 32 bits */ + mem->device_base = dev_base; + mem->size = pages; + mem->flags = flags; + dev->dma_mem = mem; + return DMA_MEMORY_MAP; + +err_bitmap: + dma_free_coherent(dev, size, mem->virt_base, dev_base); +err_alloc_coherent: + kfree(mem); +out: + return 0; +} + +static void __devexit +dt3155_free_coherent(struct device *dev) +{ + struct dma_coherent_mem *mem = dev->dma_mem; + + if (!mem) + return; + dev->dma_mem = NULL; + dma_free_coherent(dev, mem->size << PAGE_SHIFT, + mem->virt_base, mem->device_base); + kfree(mem->bitmap); + kfree(mem); +} + +static int __devinit +dt3155_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + int err; + struct dt3155_priv *pd; + + err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + return -ENODEV; + err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (err) + return -ENODEV; + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return -ENOMEM; + pd->vdev = video_device_alloc(); + if (!pd->vdev) + goto err_video_device_alloc; + *pd->vdev = dt3155_vdev; + pci_set_drvdata(pdev, pd); /* for use in dt3155_remove() */ + video_set_drvdata(pd->vdev, pd); /* for use in video_fops */ + pd->users = 0; + pd->pdev = pdev; + INIT_LIST_HEAD(&pd->dmaq); + mutex_init(&pd->mux); + pd->vdev->lock = &pd->mux; /* for locking v4l2_file_operations */ + spin_lock_init(&pd->lock); + pd->csr2 = csr2_init; + pd->config = config_init; + err = pci_enable_device(pdev); + if (err) + goto err_enable_dev; + err = pci_request_region(pdev, 0, pci_name(pdev)); + if (err) + goto err_req_region; + pd->regs = pci_iomap(pdev, 0, pci_resource_len(pd->pdev, 0)); + if (!pd->regs) + err = -ENOMEM; + goto err_pci_iomap; + err = dt3155_init_board(pdev); + if (err) + goto err_init_board; + err = video_register_device(pd->vdev, VFL_TYPE_GRABBER, -1); + if (err) + goto err_init_board; + if (dt3155_alloc_coherent(&pdev->dev, DT3155_CHUNK_SIZE, + DMA_MEMORY_MAP)) + dev_info(&pdev->dev, "preallocated 8 buffers\n"); + dev_info(&pdev->dev, "/dev/video%i is ready\n", pd->vdev->minor); + return 0; /* success */ + +err_init_board: + pci_iounmap(pdev, pd->regs); +err_pci_iomap: + pci_release_region(pdev, 0); +err_req_region: + pci_disable_device(pdev); +err_enable_dev: + video_device_release(pd->vdev); +err_video_device_alloc: + kfree(pd); + return err; +} + +static void __devexit +dt3155_remove(struct pci_dev *pdev) +{ + struct dt3155_priv *pd = pci_get_drvdata(pdev); + + dt3155_free_coherent(&pdev->dev); + video_unregister_device(pd->vdev); + pci_iounmap(pdev, pd->regs); + pci_release_region(pdev, 0); + pci_disable_device(pdev); + /* + * video_device_release() is invoked automatically + * see: struct video_device dt3155_vdev + */ + kfree(pd); +} + +static DEFINE_PCI_DEVICE_TABLE(pci_ids) = { + { PCI_DEVICE(DT3155_VENDOR_ID, DT3155_DEVICE_ID) }, + { 0, /* zero marks the end */ }, +}; +MODULE_DEVICE_TABLE(pci, pci_ids); + +static struct pci_driver pci_driver = { + .name = DT3155_NAME, + .id_table = pci_ids, + .probe = dt3155_probe, + .remove = __devexit_p(dt3155_remove), +}; + +static int __init +dt3155_init_module(void) +{ + return pci_register_driver(&pci_driver); +} + +static void __exit +dt3155_exit_module(void) +{ + pci_unregister_driver(&pci_driver); +} + +module_init(dt3155_init_module); +module_exit(dt3155_exit_module); + +MODULE_DESCRIPTION("video4linux pci-driver for dt3155 frame grabber"); +MODULE_AUTHOR("Marin Mitov "); +MODULE_VERSION(DT3155_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/media/dt3155v4l/dt3155v4l.h b/drivers/staging/media/dt3155v4l/dt3155v4l.h new file mode 100644 index 000000000000..2e4f89d402e4 --- /dev/null +++ b/drivers/staging/media/dt3155v4l/dt3155v4l.h @@ -0,0 +1,212 @@ +/*************************************************************************** + * Copyright (C) 2006-2010 by Marin Mitov * + * mitov@issp.bas.bg * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +/* DT3155 header file */ +#ifndef _DT3155_H_ +#define _DT3155_H_ + +#ifdef __KERNEL__ + +#include +#include + +#define DT3155_NAME "dt3155" +#define DT3155_VER_MAJ 1 +#define DT3155_VER_MIN 1 +#define DT3155_VER_EXT 0 +#define DT3155_VERSION __stringify(DT3155_VER_MAJ) "." \ + __stringify(DT3155_VER_MIN) "." \ + __stringify(DT3155_VER_EXT) + +/* DT3155 Base Register offsets (memory mapped) */ +#define EVEN_DMA_START 0x00 +#define ODD_DMA_START 0x0C +#define EVEN_DMA_STRIDE 0x18 +#define ODD_DMA_STRIDE 0x24 +#define EVEN_PIXEL_FMT 0x30 +#define ODD_PIXEL_FMT 0x34 +#define FIFO_TRIGER 0x38 +#define XFER_MODE 0x3C +#define CSR1 0x40 +#define RETRY_WAIT_CNT 0x44 +#define INT_CSR 0x48 +#define EVEN_FLD_MASK 0x4C +#define ODD_FLD_MASK 0x50 +#define MASK_LENGTH 0x54 +#define FIFO_FLAG_CNT 0x58 +#define IIC_CLK_DUR 0x5C +#define IIC_CSR1 0x60 +#define IIC_CSR2 0x64 + +/* DT3155 Internal Registers indexes (i2c/IIC mapped) */ +#define CSR2 0x10 +#define EVEN_CSR 0x11 +#define ODD_CSR 0x12 +#define CONFIG 0x13 +#define DT_ID 0x1F +#define X_CLIP_START 0x20 +#define Y_CLIP_START 0x22 +#define X_CLIP_END 0x24 +#define Y_CLIP_END 0x26 +#define AD_ADDR 0x30 +#define AD_LUT 0x31 +#define AD_CMD 0x32 +#define DIG_OUT 0x40 +#define PM_LUT_ADDR 0x50 +#define PM_LUT_DATA 0x51 + +/* AD command register values */ +#define AD_CMD_REG 0x00 +#define AD_POS_REF 0x01 +#define AD_NEG_REF 0x02 + +/* CSR1 bit masks */ +#define CRPT_DIS 0x00004000 +#define FLD_CRPT_ODD 0x00000200 +#define FLD_CRPT_EVEN 0x00000100 +#define FIFO_EN 0x00000080 +#define SRST 0x00000040 +#define FLD_DN_ODD 0x00000020 +#define FLD_DN_EVEN 0x00000010 +/* These should not be used. + * Use CAP_CONT_ODD/EVEN instead +#define CAP_SNGL_ODD 0x00000008 +#define CAP_SNGL_EVEN 0x00000004 +*/ +#define CAP_CONT_ODD 0x00000002 +#define CAP_CONT_EVEN 0x00000001 + +/* INT_CSR bit masks */ +#define FLD_START_EN 0x00000400 +#define FLD_END_ODD_EN 0x00000200 +#define FLD_END_EVEN_EN 0x00000100 +#define FLD_START 0x00000004 +#define FLD_END_ODD 0x00000002 +#define FLD_END_EVEN 0x00000001 + +/* IIC_CSR1 bit masks */ +#define DIRECT_ABORT 0x00000200 + +/* IIC_CSR2 bit masks */ +#define NEW_CYCLE 0x01000000 +#define DIR_RD 0x00010000 +#define IIC_READ 0x01010000 +#define IIC_WRITE 0x01000000 + +/* CSR2 bit masks */ +#define DISP_PASS 0x40 +#define BUSY_ODD 0x20 +#define BUSY_EVEN 0x10 +#define SYNC_PRESENT 0x08 +#define VT_50HZ 0x04 +#define SYNC_SNTL 0x02 +#define CHROM_FILT 0x01 +#define VT_60HZ 0x00 + +/* CSR_EVEN/ODD bit masks */ +#define CSR_ERROR 0x04 +#define CSR_SNGL 0x02 +#define CSR_DONE 0x01 + +/* CONFIG bit masks */ +#define PM_LUT_PGM 0x80 +#define PM_LUT_SEL 0x40 +#define CLIP_EN 0x20 +#define HSCALE_EN 0x10 +#define EXT_TRIG_UP 0x0C +#define EXT_TRIG_DOWN 0x04 +#define ACQ_MODE_NEXT 0x02 +#define ACQ_MODE_ODD 0x01 +#define ACQ_MODE_EVEN 0x00 + +/* AD_CMD bit masks */ +#define VIDEO_CNL_1 0x00 +#define VIDEO_CNL_2 0x40 +#define VIDEO_CNL_3 0x80 +#define VIDEO_CNL_4 0xC0 +#define SYNC_CNL_1 0x00 +#define SYNC_CNL_2 0x10 +#define SYNC_CNL_3 0x20 +#define SYNC_CNL_4 0x30 +#define SYNC_LVL_1 0x00 +#define SYNC_LVL_2 0x04 +#define SYNC_LVL_3 0x08 +#define SYNC_LVL_4 0x0C + +/* DT3155 identificator */ +#define DT3155_ID 0x20 + +#ifdef CONFIG_DT3155_CCIR +#define DMA_STRIDE 768 +#else +#define DMA_STRIDE 640 +#endif + +/** + * struct dt3155_stats - statistics structure + * + * @free_bufs_empty: no free image buffers + * @corrupted_fields: corrupted fields + * @dma_map_failed: dma mapping failed + * @start_before_end: new started before old ended + */ +struct dt3155_stats { + int free_bufs_empty; + int corrupted_fields; + int dma_map_failed; + int start_before_end; +}; + +/* per board private data structure */ +/** + * struct dt3155_priv - private data structure + * + * @vdev: pointer to video_device structure + * @pdev: pointer to pci_dev structure + * @q pointer to vb2_queue structure + * @curr_buf: pointer to curren buffer + * @mux: mutex to protect the instance + * @dmaq queue for dma buffers + * @lock spinlock for dma queue + * @field_count fields counter + * @stats: statistics structure + * @users open count + * @regs: local copy of mmio base register + * @csr2: local copy of csr2 register + * @config: local copy of config register + */ +struct dt3155_priv { + struct video_device *vdev; + struct pci_dev *pdev; + struct vb2_queue *q; + struct vb2_buffer *curr_buf; + struct mutex mux; + struct list_head dmaq; + spinlock_t lock; + unsigned int field_count; + struct dt3155_stats stats; + void __iomem *regs; + int users; + u8 csr2, config; +}; + +#endif /* __KERNEL__ */ + +#endif /* _DT3155_H_ */ diff --git a/drivers/staging/media/easycap/Kconfig b/drivers/staging/media/easycap/Kconfig new file mode 100644 index 000000000000..a425a6f9cdca --- /dev/null +++ b/drivers/staging/media/easycap/Kconfig @@ -0,0 +1,30 @@ +config EASYCAP + tristate "EasyCAP USB ID 05e1:0408 support" + depends on USB && VIDEO_DEV && SND + select SND_PCM + + ---help--- + This is an integrated audio/video driver for EasyCAP cards with + USB ID 05e1:0408. It supports two hardware variants: + + * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60, + having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R) + + * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled + 1, 2, 3, 4 and an unlabelled input cable for a microphone. + + To compile this driver as a module, choose M here: the + module will be called easycap + +config EASYCAP_DEBUG + bool "Enable EasyCAP driver debugging" + depends on EASYCAP + + ---help--- + This option enables debug printouts + + To enable debug, pass the debug level to the debug module + parameter: + + modprobe easycap debug=[0..9] + diff --git a/drivers/staging/media/easycap/Makefile b/drivers/staging/media/easycap/Makefile new file mode 100644 index 000000000000..a34e75f59c18 --- /dev/null +++ b/drivers/staging/media/easycap/Makefile @@ -0,0 +1,10 @@ +easycap-objs := easycap_main.o +easycap-objs += easycap_low.o +easycap-objs += easycap_ioctl.o +easycap-objs += easycap_settings.o +easycap-objs += easycap_testcard.o +easycap-objs += easycap_sound.o +obj-$(CONFIG_EASYCAP) += easycap.o + +ccflags-y := -Wall + diff --git a/drivers/staging/media/easycap/README b/drivers/staging/media/easycap/README new file mode 100644 index 000000000000..796b032384bd --- /dev/null +++ b/drivers/staging/media/easycap/README @@ -0,0 +1,141 @@ + + *********************************************************** + * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60 * + * and * + * EasyCAP002 4-Channel USB 2.0 DVR * + *********************************************************** + Mike Thomas + + + +SUPPORTED HARDWARE +------------------ + +This driver is intended for use with hardware having USB ID 05e1:0408. +Two kinds of EasyCAP have this USB ID, namely: + + * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60, + having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R) + + * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled + 1, 2, 3, 4 and an unlabelled input cable for a microphone. + + +BUILD OPTIONS AND DEPENDENCIES +------------------------------ + +Unless EASYCAP_DEBUG is defined during compilation it will not be possible +to select a debug level at the time of module installation. + + +KNOWN RUNTIME ISSUES +-------------------- + +(1) Intentionally, this driver will not stream material which is unambiguously +identified by the hardware as copy-protected. Normal video output will be +present for about a minute but will then freeze when this situation arises. + +(2) The controls for luminance, contrast, saturation, hue and volume may not +always work properly. + +(3) Reduced-resolution S-Video seems to suffer from moire artefacts. + + +INPUT NUMBERING +--------------- + +For the EasyCAP with S-VIDEO input cable the driver regards a request for +inputs numbered 0 or 1 as referring to CVBS and a request for input +numbered 5 as referring to S-VIDEO. + +For the EasyCAP with four CVBS inputs the driver expects to be asked for +any one of inputs numbered 1,2,3,4. If input 0 is asked for, it is +interpreted as input 1. + + +MODULE PARAMETERS +----------------- + +Three module parameters are defined: + +debug the easycap module is configured at diagnostic level n (0 to 9) +gain audio gain level n (0 to 31, default is 16) +bars whether to display testcard bars when incoming video signal is lost + 0 => no, 1 => yes (default) + + +SUPPORTED TV STANDARDS AND RESOLUTIONS +-------------------------------------- + +The following TV standards are natively supported by the hardware and are +usable as (for example) the "norm=" parameter in the mplayer command: + + PAL_BGHIN, NTSC_N_443, + PAL_Nc, NTSC_N, + SECAM, NTSC_M, NTSC_M_JP, + PAL_60, NTSC_443, + PAL_M. + +In addition, the driver offers "custom" pseudo-standards with a framerate +which is 20% of the usual framerate. These pseudo-standards are named: + + PAL_BGHIN_SLOW, NTSC_N_443_SLOW, + PAL_Nc_SLOW, NTSC_N_SLOW, + SECAM_SLOW, NTSC_M_SLOW, NTSC_M_JP_SLOW, + PAL_60_SLOW, NTSC_443_SLOW, + PAL_M_SLOW. + + +The available picture sizes are: + + at 25 frames per second: 720x576, 704x576, 640x480, 360x288, 320x240; + at 30 frames per second: 720x480, 640x480, 360x240, 320x240. + + +WHAT'S TESTED AND WHAT'S NOT +---------------------------- + +This driver is known to work with mplayer, mencoder, tvtime, zoneminder, +xawtv, gstreamer and sufficiently recent versions of vlc. An interface +to ffmpeg is implemented, but serious audio-video synchronization problems +remain. + +The driver is designed to support all the TV standards accepted by the +hardware, but as yet it has actually been tested on only a few of these. + +I have been unable to test and calibrate the S-video input myself because I +do not possess any equipment with S-video output. + + +UDEV RULES +---------- + +In order that the special files /dev/easycap0 and /dev/easysnd1 are created +with conveniently relaxed permissions when the EasyCAP is plugged in, a file +is preferably to be provided in directory /etc/udev/rules.d with content: + +ACTION!="add|change", GOTO="easycap_rules_end" +ATTRS{idVendor}=="05e1", ATTRS{idProduct}=="0408", \ + MODE="0666", OWNER="root", GROUP="root" +LABEL="easycap_rules_end" + + +MODPROBE CONFIGURATION +---------------------- + +The easycap module is in competition with the module snd-usb-audio for the +EasyCAP's audio channel, and its installation can be aided by providing a +file in directory /etc/modprobe.d with content: + +options easycap gain=16 bars=1 +install easycap /sbin/rmmod snd-usb-audio; /sbin/modprobe --ignore-install easycap + + +ACKNOWLEGEMENTS AND REFERENCES +------------------------------ +This driver makes use of information contained in the Syntek Semicon DC-1125 +Driver, presently maintained at http://sourceforge.net/projects/syntekdriver/ +by Nicolas Vivien. Particularly useful has been a patch to the latter driver +provided by Ivor Hewitt in January 2009. The NTSC implementation is taken +from the work of Ben Trask. + diff --git a/drivers/staging/media/easycap/easycap.h b/drivers/staging/media/easycap/easycap.h new file mode 100644 index 000000000000..7b256a948c27 --- /dev/null +++ b/drivers/staging/media/easycap/easycap.h @@ -0,0 +1,594 @@ +/***************************************************************************** +* * +* easycap.h * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * THE FOLLOWING PARAMETERS ARE UNDEFINED: + * + * EASYCAP_DEBUG + * + * IF REQUIRED THEY MUST BE EXTERNALLY DEFINED, FOR EXAMPLE AS COMPILER + * OPTIONS. + */ +/*---------------------------------------------------------------------------*/ + +#ifndef __EASYCAP_H__ +#define __EASYCAP_H__ + +/*---------------------------------------------------------------------------*/ +/* + * THESE ARE NORMALLY DEFINED + */ +/*---------------------------------------------------------------------------*/ +#define PATIENCE 500 +#define PERSEVERE +/*---------------------------------------------------------------------------*/ +/* + * THESE ARE FOR MAINTENANCE ONLY - NORMALLY UNDEFINED: + */ +/*---------------------------------------------------------------------------*/ +#undef EASYCAP_TESTCARD +/*---------------------------------------------------------------------------*/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/*---------------------------------------------------------------------------*/ +/* VENDOR, PRODUCT: Syntek Semiconductor Co., Ltd + * + * EITHER EasyCAP USB 2.0 Video Adapter with Audio, Model No. DC60 + * with input cabling: AUDIO(L), AUDIO(R), CVBS, S-VIDEO. + * + * OR EasyCAP 4CHANNEL USB 2.0 DVR, Model No. EasyCAP002 + * with input cabling: MICROPHONE, CVBS1, CVBS2, CVBS3, CVBS4. + */ +/*---------------------------------------------------------------------------*/ +#define USB_EASYCAP_VENDOR_ID 0x05e1 +#define USB_EASYCAP_PRODUCT_ID 0x0408 + +#define EASYCAP_DRIVER_VERSION "0.9.01" +#define EASYCAP_DRIVER_DESCRIPTION "easycapdc60" + +#define USB_SKEL_MINOR_BASE 192 +#define DONGLE_MANY 8 +#define INPUT_MANY 6 +/*---------------------------------------------------------------------------*/ +/* + * DEFAULT LUMINANCE, CONTRAST, SATURATION AND HUE + */ +/*---------------------------------------------------------------------------*/ +#define SAA_0A_DEFAULT 0x7F +#define SAA_0B_DEFAULT 0x3F +#define SAA_0C_DEFAULT 0x2F +#define SAA_0D_DEFAULT 0x00 +/*---------------------------------------------------------------------------*/ +/* + * VIDEO STREAMING PARAMETERS: + * USB 2.0 PROVIDES FOR HIGH-BANDWIDTH ENDPOINTS WITH AN UPPER LIMIT + * OF 3072 BYTES PER MICROFRAME for wMaxPacketSize. + */ +/*---------------------------------------------------------------------------*/ +#define VIDEO_ISOC_BUFFER_MANY 16 +#define VIDEO_ISOC_ORDER 3 +#define VIDEO_ISOC_FRAMESPERDESC ((unsigned int) 1 << VIDEO_ISOC_ORDER) +#define USB_2_0_MAXPACKETSIZE 3072 +#if (USB_2_0_MAXPACKETSIZE > PAGE_SIZE) +#error video_isoc_buffer[.] will not be big enough +#endif +#define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY +#define VIDEO_LOST_TOLERATE 50 +/*---------------------------------------------------------------------------*/ +/* + * VIDEO BUFFERS + */ +/*---------------------------------------------------------------------------*/ +#define FIELD_BUFFER_SIZE (203 * PAGE_SIZE) +#define FRAME_BUFFER_SIZE (405 * PAGE_SIZE) +#define FIELD_BUFFER_MANY 4 +#define FRAME_BUFFER_MANY 6 +/*---------------------------------------------------------------------------*/ +/* + * AUDIO STREAMING PARAMETERS + */ +/*---------------------------------------------------------------------------*/ +#define AUDIO_ISOC_BUFFER_MANY 16 +#define AUDIO_ISOC_ORDER 1 +#define AUDIO_ISOC_FRAMESPERDESC 32 +#define AUDIO_ISOC_BUFFER_SIZE (PAGE_SIZE << AUDIO_ISOC_ORDER) +/*---------------------------------------------------------------------------*/ +/* + * AUDIO BUFFERS + */ +/*---------------------------------------------------------------------------*/ +#define AUDIO_FRAGMENT_MANY 32 +#define PAGES_PER_AUDIO_FRAGMENT 4 +/*---------------------------------------------------------------------------*/ +/* + * IT IS ESSENTIAL THAT EVEN-NUMBERED STANDARDS ARE 25 FRAMES PER SECOND, + * ODD-NUMBERED STANDARDS ARE 30 FRAMES PER SECOND. + * THE NUMBERING OF STANDARDS MUST NOT BE CHANGED WITHOUT DUE CARE. NOT + * ONLY MUST THE PARAMETER + * STANDARD_MANY + * BE CHANGED TO CORRESPOND TO THE NEW NUMBER OF STANDARDS, BUT ALSO THE + * NUMBERING MUST REMAIN AN UNBROKEN ASCENDING SEQUENCE: DUMMY STANDARDS + * MAY NEED TO BE ADDED. APPROPRIATE CHANGES WILL ALWAYS BE REQUIRED IN + * ROUTINE fillin_formats() AND POSSIBLY ELSEWHERE. BEWARE. + */ +/*---------------------------------------------------------------------------*/ +#define PAL_BGHIN 0 +#define PAL_Nc 2 +#define SECAM 4 +#define NTSC_N 6 +#define NTSC_N_443 8 +#define NTSC_M 1 +#define NTSC_443 3 +#define NTSC_M_JP 5 +#define PAL_60 7 +#define PAL_M 9 +#define PAL_BGHIN_SLOW 10 +#define PAL_Nc_SLOW 12 +#define SECAM_SLOW 14 +#define NTSC_N_SLOW 16 +#define NTSC_N_443_SLOW 18 +#define NTSC_M_SLOW 11 +#define NTSC_443_SLOW 13 +#define NTSC_M_JP_SLOW 15 +#define PAL_60_SLOW 17 +#define PAL_M_SLOW 19 +#define STANDARD_MANY 20 +/*---------------------------------------------------------------------------*/ +/* + * ENUMS + */ +/*---------------------------------------------------------------------------*/ +enum { + AT_720x576, + AT_704x576, + AT_640x480, + AT_720x480, + AT_360x288, + AT_320x240, + AT_360x240, + RESOLUTION_MANY +}; +enum { + FMT_UYVY, + FMT_YUY2, + FMT_RGB24, + FMT_RGB32, + FMT_BGR24, + FMT_BGR32, + PIXELFORMAT_MANY +}; +enum { + FIELD_NONE, + FIELD_INTERLACED, + INTERLACE_MANY +}; +#define SETTINGS_MANY (STANDARD_MANY * \ + RESOLUTION_MANY * \ + 2 * \ + PIXELFORMAT_MANY * \ + INTERLACE_MANY) +/*---------------------------------------------------------------------------*/ +/* + * STRUCTURE DEFINITIONS + */ +/*---------------------------------------------------------------------------*/ +struct easycap_dongle { + struct easycap *peasycap; + struct mutex mutex_video; + struct mutex mutex_audio; +}; +/*---------------------------------------------------------------------------*/ +struct data_buffer { + struct list_head list_head; + void *pgo; + void *pto; + u16 kount; + u16 input; +}; +/*---------------------------------------------------------------------------*/ +struct data_urb { + struct list_head list_head; + struct urb *purb; + int isbuf; + int length; +}; +/*---------------------------------------------------------------------------*/ +struct easycap_standard { + u16 mask; +struct v4l2_standard v4l2_standard; +}; +struct easycap_format { + u16 mask; + char name[128]; +struct v4l2_format v4l2_format; +}; +struct inputset { + int input; + int input_ok; + int standard_offset; + int standard_offset_ok; + int format_offset; + int format_offset_ok; + int brightness; + int brightness_ok; + int contrast; + int contrast_ok; + int saturation; + int saturation_ok; + int hue; + int hue_ok; +}; +/*---------------------------------------------------------------------------*/ +/* + * easycap.ilk == 0 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=256 + * easycap.ilk == 2 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=9 + * easycap.ilk == 3 => FOUR-CVBS HARDWARE, AUDIO wMaxPacketSize=9 + */ +/*---------------------------------------------------------------------------*/ +struct easycap { + int isdongle; + int minor; + + struct video_device video_device; + struct v4l2_device v4l2_device; + + int status; + unsigned int audio_pages_per_fragment; + unsigned int audio_bytes_per_fragment; + unsigned int audio_buffer_page_many; + +#define UPSAMPLE +#ifdef UPSAMPLE + s16 oldaudio; +#endif /*UPSAMPLE*/ + + int ilk; + bool microphone; + + struct usb_device *pusb_device; + struct usb_interface *pusb_interface; + + struct kref kref; + + int queued[FRAME_BUFFER_MANY]; + int done[FRAME_BUFFER_MANY]; + + wait_queue_head_t wq_video; + wait_queue_head_t wq_audio; + wait_queue_head_t wq_trigger; + + int input; + int polled; + int standard_offset; + int format_offset; + struct inputset inputset[INPUT_MANY]; + + bool ntsc; + int fps; + int usec; + int tolerate; + int skip; + int skipped; + int lost[INPUT_MANY]; + int merit[180]; + + long long int dnbydt; + + int video_interface; + int video_altsetting_on; + int video_altsetting_off; + int video_endpointnumber; + int video_isoc_maxframesize; + int video_isoc_buffer_size; + int video_isoc_framesperdesc; + + int video_isoc_streaming; + int video_isoc_sequence; + int video_idle; + int video_eof; + int video_junk; + + struct data_buffer video_isoc_buffer[VIDEO_ISOC_BUFFER_MANY]; + struct data_buffer field_buffer[FIELD_BUFFER_MANY] + [(FIELD_BUFFER_SIZE/PAGE_SIZE)]; + struct data_buffer frame_buffer[FRAME_BUFFER_MANY] + [(FRAME_BUFFER_SIZE/PAGE_SIZE)]; + + struct list_head urb_video_head; + struct list_head *purb_video_head; + + u8 cache[8]; + u8 *pcache; + int video_mt; + int audio_mt; + long long audio_bytes; + u32 isequence; + + int vma_many; +/*---------------------------------------------------------------------------*/ +/* + * BUFFER INDICATORS + */ +/*---------------------------------------------------------------------------*/ + int field_fill; /* Field buffer being filled by easycap_complete(). */ + /* Bumped only by easycap_complete(). */ + int field_page; /* Page of field buffer page being filled by */ + /* easycap_complete(). */ + int field_read; /* Field buffer to be read by field2frame(). */ + /* Bumped only by easycap_complete(). */ + int frame_fill; /* Frame buffer being filled by field2frame(). */ + /* Bumped only by easycap_dqbuf() when */ + /* field2frame() has created a complete frame. */ + int frame_read; /* Frame buffer offered to user by DQBUF. */ + /* Set only by easycap_dqbuf() to trail frame_fill.*/ + int frame_lock; /* Flag set to 1 by DQBUF and cleared by QBUF */ +/*---------------------------------------------------------------------------*/ +/* + * IMAGE PROPERTIES + */ +/*---------------------------------------------------------------------------*/ + u32 pixelformat; + int width; + int height; + int bytesperpixel; + bool byteswaporder; + bool decimatepixel; + bool offerfields; + int frame_buffer_used; + int frame_buffer_many; + int videofieldamount; + + int brightness; + int contrast; + int saturation; + int hue; + + int allocation_video_urb; + int allocation_video_page; + int allocation_video_struct; + int registered_video; +/*---------------------------------------------------------------------------*/ +/* + * ALSA + */ +/*---------------------------------------------------------------------------*/ + struct snd_pcm_hardware alsa_hardware; + struct snd_card *psnd_card; + struct snd_pcm *psnd_pcm; + struct snd_pcm_substream *psubstream; + int dma_fill; + int dma_next; + int dma_read; +/*---------------------------------------------------------------------------*/ +/* + * SOUND PROPERTIES + */ +/*---------------------------------------------------------------------------*/ + int audio_interface; + int audio_altsetting_on; + int audio_altsetting_off; + int audio_endpointnumber; + int audio_isoc_maxframesize; + int audio_isoc_buffer_size; + int audio_isoc_framesperdesc; + + int audio_isoc_streaming; + int audio_idle; + int audio_eof; + int volume; + int mute; + s8 gain; + + struct data_buffer audio_isoc_buffer[AUDIO_ISOC_BUFFER_MANY]; + + struct list_head urb_audio_head; + struct list_head *purb_audio_head; +/*---------------------------------------------------------------------------*/ +/* + * BUFFER INDICATORS + */ +/*---------------------------------------------------------------------------*/ + int audio_fill; /* Audio buffer being filled by easycap_complete(). */ + /* Bumped only by easycap_complete(). */ + int audio_read; /* Audio buffer page being read by easycap_read(). */ + /* Set by easycap_read() to trail audio_fill by */ + /* one fragment. */ +/*---------------------------------------------------------------------------*/ +/* + * SOUND PROPERTIES + */ +/*---------------------------------------------------------------------------*/ + + int audio_buffer_many; + + int allocation_audio_urb; + int allocation_audio_page; + int allocation_audio_struct; + int registered_audio; + + long long int audio_sample; + long long int audio_niveau; + long long int audio_square; + + struct data_buffer audio_buffer[]; +}; +/*---------------------------------------------------------------------------*/ +/* + * VIDEO FUNCTION PROTOTYPES + */ +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +long easycap_unlocked_ioctl(struct file *, unsigned int, unsigned long); +int easycap_dqbuf(struct easycap *, int); +int submit_video_urbs(struct easycap *); +int kill_video_urbs(struct easycap *); +int field2frame(struct easycap *); +int redaub(struct easycap *, void *, void *, + int, int, u8, u8, bool); +void easycap_testcard(struct easycap *, int); +int fillin_formats(void); +int newinput(struct easycap *, int); +int adjust_standard(struct easycap *, v4l2_std_id); +int adjust_format(struct easycap *, u32, u32, u32, + int, bool); +int adjust_brightness(struct easycap *, int); +int adjust_contrast(struct easycap *, int); +int adjust_saturation(struct easycap *, int); +int adjust_hue(struct easycap *, int); +int adjust_volume(struct easycap *, int); +/*---------------------------------------------------------------------------*/ +/* + * AUDIO FUNCTION PROTOTYPES + */ +/*---------------------------------------------------------------------------*/ +int easycap_alsa_probe(struct easycap *); +void easycap_alsa_complete(struct urb *); + +int easycap_sound_setup(struct easycap *); +int submit_audio_urbs(struct easycap *); +int kill_audio_urbs(struct easycap *); +void easyoss_testtone(struct easycap *, int); +int audio_setup(struct easycap *); +/*---------------------------------------------------------------------------*/ +/* + * LOW-LEVEL FUNCTION PROTOTYPES + */ +/*---------------------------------------------------------------------------*/ +int audio_gainget(struct usb_device *); +int audio_gainset(struct usb_device *, s8); + +int set_interface(struct usb_device *, u16); +int wakeup_device(struct usb_device *); +int confirm_resolution(struct usb_device *); +int confirm_stream(struct usb_device *); + +int setup_stk(struct usb_device *, bool); +int setup_saa(struct usb_device *, bool); +int setup_vt(struct usb_device *); +int check_stk(struct usb_device *, bool); +int check_saa(struct usb_device *, bool); +int ready_saa(struct usb_device *); +int merit_saa(struct usb_device *); +int check_vt(struct usb_device *); +int select_input(struct usb_device *, int, int); +int set_resolution(struct usb_device *, + u16, u16, u16, u16); + +int read_saa(struct usb_device *, u16); +int read_stk(struct usb_device *, u32); +int write_saa(struct usb_device *, u16, u16); +int write_000(struct usb_device *, u16, u16); +int start_100(struct usb_device *); +int stop_100(struct usb_device *); +int write_300(struct usb_device *); +int read_vt(struct usb_device *, u16); +int write_vt(struct usb_device *, u16, u16); +int isdongle(struct easycap *); +/*---------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------*/ +/* + * MACROS SAM(...) AND JOM(...) ALLOW DIAGNOSTIC OUTPUT TO BE TAGGED WITH + * THE IDENTITY OF THE DONGLE TO WHICH IT APPLIES, BUT IF INVOKED WHEN THE + * POINTER peasycap IS INVALID AN Oops IS LIKELY, AND ITS CAUSE MAY NOT BE + * IMMEDIATELY OBVIOUS FROM A CASUAL READING OF THE SOURCE CODE. BEWARE. +*/ +/*---------------------------------------------------------------------------*/ +const char *strerror(int err); + +#define SAY(format, args...) do { \ + printk(KERN_DEBUG "easycap:: %s: " \ + format, __func__, ##args); \ +} while (0) +#define SAM(format, args...) do { \ + printk(KERN_DEBUG "easycap::%i%s: " \ + format, peasycap->isdongle, __func__, ##args);\ +} while (0) + +#ifdef CONFIG_EASYCAP_DEBUG +extern int easycap_debug; +#define JOT(n, format, args...) do { \ + if (n <= easycap_debug) { \ + printk(KERN_DEBUG "easycap:: %s: " \ + format, __func__, ##args);\ + } \ +} while (0) +#define JOM(n, format, args...) do { \ + if (n <= easycap_debug) { \ + printk(KERN_DEBUG "easycap::%i%s: " \ + format, peasycap->isdongle, __func__, ##args);\ + } \ +} while (0) + +#else +#define JOT(n, format, args...) do {} while (0) +#define JOM(n, format, args...) do {} while (0) +#endif /* CONFIG_EASYCAP_DEBUG */ + +/*---------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------*/ +/* globals + */ +/*---------------------------------------------------------------------------*/ + +extern bool easycap_readback; +extern const struct easycap_standard easycap_standard[]; +extern struct easycap_format easycap_format[]; +extern struct v4l2_queryctrl easycap_control[]; +extern struct usb_driver easycap_usb_driver; +extern struct easycap_dongle easycapdc60_dongle[]; + +#endif /* !__EASYCAP_H__ */ diff --git a/drivers/staging/media/easycap/easycap_ioctl.c b/drivers/staging/media/easycap/easycap_ioctl.c new file mode 100644 index 000000000000..c99addfb6242 --- /dev/null +++ b/drivers/staging/media/easycap/easycap_ioctl.c @@ -0,0 +1,2450 @@ +/****************************************************************************** +* * +* easycap_ioctl.c * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +/*****************************************************************************/ + +#include +#include "easycap.h" + +/*--------------------------------------------------------------------------*/ +/* + * UNLESS THERE IS A PREMATURE ERROR RETURN THIS ROUTINE UPDATES THE + * FOLLOWING: + * peasycap->standard_offset + * peasycap->inputset[peasycap->input].standard_offset + * peasycap->fps + * peasycap->usec + * peasycap->tolerate + * peasycap->skip + */ +/*---------------------------------------------------------------------------*/ +int adjust_standard(struct easycap *peasycap, v4l2_std_id std_id) +{ + struct easycap_standard const *peasycap_standard; + u16 reg, set; + int ir, rc, need, k; + unsigned int itwas, isnow; + bool resubmit; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + peasycap_standard = &easycap_standard[0]; + while (0xFFFF != peasycap_standard->mask) { + if (std_id == peasycap_standard->v4l2_standard.id) + break; + peasycap_standard++; + } + if (0xFFFF == peasycap_standard->mask) { + peasycap_standard = &easycap_standard[0]; + while (0xFFFF != peasycap_standard->mask) { + if (std_id & peasycap_standard->v4l2_standard.id) + break; + peasycap_standard++; + } + } + if (0xFFFF == peasycap_standard->mask) { + SAM("ERROR: 0x%08X=std_id: standard not found\n", + (unsigned int)std_id); + return -EINVAL; + } + SAM("selected standard: %s\n", + &(peasycap_standard->v4l2_standard.name[0])); + if (peasycap->standard_offset == peasycap_standard - easycap_standard) { + SAM("requested standard already in effect\n"); + return 0; + } + peasycap->standard_offset = peasycap_standard - easycap_standard; + for (k = 0; k < INPUT_MANY; k++) { + if (!peasycap->inputset[k].standard_offset_ok) { + peasycap->inputset[k].standard_offset = + peasycap->standard_offset; + } + } + if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { + peasycap->inputset[peasycap->input].standard_offset = + peasycap->standard_offset; + peasycap->inputset[peasycap->input].standard_offset_ok = 1; + } else + JOM(8, "%i=peasycap->input\n", peasycap->input); + + peasycap->fps = peasycap_standard->v4l2_standard.frameperiod.denominator / + peasycap_standard->v4l2_standard.frameperiod.numerator; + switch (peasycap->fps) { + case 6: + case 30: { + peasycap->ntsc = true; + break; + } + case 5: + case 25: { + peasycap->ntsc = false; + break; + } + default: { + SAM("MISTAKE: %i=frames-per-second\n", peasycap->fps); + return -ENOENT; + } + } + JOM(8, "%i frames-per-second\n", peasycap->fps); + if (0x8000 & peasycap_standard->mask) { + peasycap->skip = 5; + peasycap->usec = 1000000 / (2 * (5 * peasycap->fps)); + peasycap->tolerate = 1000 * (25 / (5 * peasycap->fps)); + } else { + peasycap->skip = 0; + peasycap->usec = 1000000 / (2 * peasycap->fps); + peasycap->tolerate = 1000 * (25 / peasycap->fps); + } + if (peasycap->video_isoc_streaming) { + resubmit = true; + kill_video_urbs(peasycap); + } else + resubmit = false; +/*--------------------------------------------------------------------------*/ +/* + * SAA7113H DATASHEET PAGE 44, TABLE 42 + */ +/*--------------------------------------------------------------------------*/ + need = 0; + itwas = 0; + reg = 0x00; + set = 0x00; + switch (peasycap_standard->mask & 0x000F) { + case NTSC_M_JP: { + reg = 0x0A; + set = 0x95; + ir = read_saa(peasycap->pusb_device, reg); + if (0 > ir) + SAM("ERROR: cannot read SAA register 0x%02X\n", reg); + else + itwas = (unsigned int)ir; + rc = write_saa(peasycap->pusb_device, reg, set); + if (rc) + SAM("ERROR: failed to set SAA register " + "0x%02X to 0x%02X for JP standard\n", reg, set); + else { + isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); + if (0 > ir) + JOM(8, "SAA register 0x%02X changed " + "to 0x%02X\n", reg, isnow); + else + JOM(8, "SAA register 0x%02X changed " + "from 0x%02X to 0x%02X\n", reg, itwas, isnow); + } + + reg = 0x0B; + set = 0x48; + ir = read_saa(peasycap->pusb_device, reg); + if (0 > ir) + SAM("ERROR: cannot read SAA register 0x%02X\n", reg); + else + itwas = (unsigned int)ir; + rc = write_saa(peasycap->pusb_device, reg, set); + if (rc) + SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X " + "for JP standard\n", reg, set); + else { + isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); + if (0 > ir) + JOM(8, "SAA register 0x%02X changed " + "to 0x%02X\n", reg, isnow); + else + JOM(8, "SAA register 0x%02X changed " + "from 0x%02X to 0x%02X\n", reg, itwas, isnow); + } +/*--------------------------------------------------------------------------*/ +/* + * NOTE: NO break HERE: RUN ON TO NEXT CASE + */ +/*--------------------------------------------------------------------------*/ + } + case NTSC_M: + case PAL_BGHIN: { + reg = 0x0E; + set = 0x01; + need = 1; + break; + } + case NTSC_N_443: + case PAL_60: { + reg = 0x0E; + set = 0x11; + need = 1; + break; + } + case NTSC_443: + case PAL_Nc: { + reg = 0x0E; + set = 0x21; + need = 1; + break; + } + case NTSC_N: + case PAL_M: { + reg = 0x0E; + set = 0x31; + need = 1; + break; + } + case SECAM: { + reg = 0x0E; + set = 0x51; + need = 1; + break; + } + default: + break; + } +/*--------------------------------------------------------------------------*/ + if (need) { + ir = read_saa(peasycap->pusb_device, reg); + if (0 > ir) + SAM("ERROR: failed to read SAA register 0x%02X\n", reg); + else + itwas = (unsigned int)ir; + rc = write_saa(peasycap->pusb_device, reg, set); + if (0 != write_saa(peasycap->pusb_device, reg, set)) { + SAM("ERROR: failed to set SAA register " + "0x%02X to 0x%02X for table 42\n", reg, set); + } else { + isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); + if (0 > ir) + JOM(8, "SAA register 0x%02X changed " + "to 0x%02X\n", reg, isnow); + else + JOM(8, "SAA register 0x%02X changed " + "from 0x%02X to 0x%02X\n", reg, itwas, isnow); + } + } +/*--------------------------------------------------------------------------*/ +/* + * SAA7113H DATASHEET PAGE 41 + */ +/*--------------------------------------------------------------------------*/ + reg = 0x08; + ir = read_saa(peasycap->pusb_device, reg); + if (0 > ir) + SAM("ERROR: failed to read SAA register 0x%02X " + "so cannot reset\n", reg); + else { + itwas = (unsigned int)ir; + if (peasycap_standard->mask & 0x0001) + set = itwas | 0x40 ; + else + set = itwas & ~0x40 ; + rc = write_saa(peasycap->pusb_device, reg, set); + if (rc) + SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n", + reg, set); + else { + isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); + if (0 > ir) + JOM(8, "SAA register 0x%02X changed to 0x%02X\n", + reg, isnow); + else + JOM(8, "SAA register 0x%02X changed " + "from 0x%02X to 0x%02X\n", reg, itwas, isnow); + } + } +/*--------------------------------------------------------------------------*/ +/* + * SAA7113H DATASHEET PAGE 51, TABLE 57 + */ +/*---------------------------------------------------------------------------*/ + reg = 0x40; + ir = read_saa(peasycap->pusb_device, reg); + if (0 > ir) + SAM("ERROR: failed to read SAA register 0x%02X " + "so cannot reset\n", reg); + else { + itwas = (unsigned int)ir; + if (peasycap_standard->mask & 0x0001) + set = itwas | 0x80 ; + else + set = itwas & ~0x80 ; + rc = write_saa(peasycap->pusb_device, reg, set); + if (rc) + SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n", + reg, set); + else { + isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); + if (0 > ir) + JOM(8, "SAA register 0x%02X changed to 0x%02X\n", + reg, isnow); + else + JOM(8, "SAA register 0x%02X changed " + "from 0x%02X to 0x%02X\n", reg, itwas, isnow); + } + } +/*--------------------------------------------------------------------------*/ +/* + * SAA7113H DATASHEET PAGE 53, TABLE 66 + */ +/*--------------------------------------------------------------------------*/ + reg = 0x5A; + ir = read_saa(peasycap->pusb_device, reg); + if (0 > ir) + SAM("ERROR: failed to read SAA register 0x%02X but continuing\n", reg); + itwas = (unsigned int)ir; + if (peasycap_standard->mask & 0x0001) + set = 0x0A ; + else + set = 0x07 ; + if (0 != write_saa(peasycap->pusb_device, reg, set)) + SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n", + reg, set); + else { + isnow = (unsigned int)read_saa(peasycap->pusb_device, reg); + if (0 > ir) + JOM(8, "SAA register 0x%02X changed " + "to 0x%02X\n", reg, isnow); + else + JOM(8, "SAA register 0x%02X changed " + "from 0x%02X to 0x%02X\n", reg, itwas, isnow); + } + if (resubmit) + submit_video_urbs(peasycap); + return 0; +} +/*****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * THE ALGORITHM FOR RESPONDING TO THE VIDIO_S_FMT IOCTL REQUIRES + * A VALID VALUE OF peasycap->standard_offset, OTHERWISE -EBUSY IS RETURNED. + * + * PROVIDED THE ARGUMENT try IS false AND THERE IS NO PREMATURE ERROR RETURN + * THIS ROUTINE UPDATES THE FOLLOWING: + * peasycap->format_offset + * peasycap->inputset[peasycap->input].format_offset + * peasycap->pixelformat + * peasycap->height + * peasycap->width + * peasycap->bytesperpixel + * peasycap->byteswaporder + * peasycap->decimatepixel + * peasycap->frame_buffer_used + * peasycap->videofieldamount + * peasycap->offerfields + * + * IF SUCCESSFUL THE FUNCTION RETURNS THE OFFSET IN easycap_format[] + * IDENTIFYING THE FORMAT WHICH IS TO RETURNED TO THE USER. + * ERRORS RETURN A NEGATIVE NUMBER. + */ +/*--------------------------------------------------------------------------*/ +int adjust_format(struct easycap *peasycap, + u32 width, u32 height, u32 pixelformat, int field, bool try) +{ + struct easycap_format *peasycap_format, *peasycap_best_format; + u16 mask; + struct usb_device *p; + int miss, multiplier, best, k; + char bf[5], fo[32], *pc; + u32 uc; + bool resubmit; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (0 > peasycap->standard_offset) { + JOM(8, "%i=peasycap->standard_offset\n", peasycap->standard_offset); + return -EBUSY; + } + p = peasycap->pusb_device; + if (!p) { + SAM("ERROR: peaycap->pusb_device is NULL\n"); + return -EFAULT; + } + pc = &bf[0]; + uc = pixelformat; + memcpy((void *)pc, (void *)(&uc), 4); + bf[4] = 0; + mask = 0xFF & easycap_standard[peasycap->standard_offset].mask; + SAM("sought: %ix%i,%s(0x%08X),%i=field,0x%02X=std mask\n", + width, height, pc, pixelformat, field, mask); + switch (field) { + case V4L2_FIELD_ANY: { + strcpy(&fo[0], "V4L2_FIELD_ANY "); + break; + } + case V4L2_FIELD_NONE: { + strcpy(&fo[0], "V4L2_FIELD_NONE"); + break; + } + case V4L2_FIELD_TOP: { + strcpy(&fo[0], "V4L2_FIELD_TOP"); + break; + } + case V4L2_FIELD_BOTTOM: { + strcpy(&fo[0], "V4L2_FIELD_BOTTOM"); + break; + } + case V4L2_FIELD_INTERLACED: { + strcpy(&fo[0], "V4L2_FIELD_INTERLACED"); + break; + } + case V4L2_FIELD_SEQ_TB: { + strcpy(&fo[0], "V4L2_FIELD_SEQ_TB"); + break; + } + case V4L2_FIELD_SEQ_BT: { + strcpy(&fo[0], "V4L2_FIELD_SEQ_BT"); + break; + } + case V4L2_FIELD_ALTERNATE: { + strcpy(&fo[0], "V4L2_FIELD_ALTERNATE"); + break; + } + case V4L2_FIELD_INTERLACED_TB: { + strcpy(&fo[0], "V4L2_FIELD_INTERLACED_TB"); + break; + } + case V4L2_FIELD_INTERLACED_BT: { + strcpy(&fo[0], "V4L2_FIELD_INTERLACED_BT"); + break; + } + default: { + strcpy(&fo[0], "V4L2_FIELD_... UNKNOWN "); + break; + } + } + SAM("sought: %s\n", &fo[0]); + if (V4L2_FIELD_ANY == field) { + field = V4L2_FIELD_NONE; + SAM("prefer: V4L2_FIELD_NONE=field, was V4L2_FIELD_ANY\n"); + } + peasycap_best_format = NULL; + peasycap_format = &easycap_format[0]; + while (0 != peasycap_format->v4l2_format.fmt.pix.width) { + JOM(16, ".> %i %i 0x%08X %ix%i\n", + peasycap_format->mask & 0x01, + peasycap_format->v4l2_format.fmt.pix.field, + peasycap_format->v4l2_format.fmt.pix.pixelformat, + peasycap_format->v4l2_format.fmt.pix.width, + peasycap_format->v4l2_format.fmt.pix.height); + + if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) && + (peasycap_format->v4l2_format.fmt.pix.field == field) && + (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat) && + (peasycap_format->v4l2_format.fmt.pix.width == width) && + (peasycap_format->v4l2_format.fmt.pix.height == height)) { + + peasycap_best_format = peasycap_format; + break; + } + peasycap_format++; + } + if (0 == peasycap_format->v4l2_format.fmt.pix.width) { + SAM("cannot do: %ix%i with standard mask 0x%02X\n", + width, height, mask); + peasycap_format = &easycap_format[0]; + best = -1; + while (0 != peasycap_format->v4l2_format.fmt.pix.width) { + if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) && + (peasycap_format->v4l2_format.fmt.pix.field == field) && + (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat)) { + + miss = abs(peasycap_format->v4l2_format.fmt.pix.width - width); + if ((best > miss) || (best < 0)) { + best = miss; + peasycap_best_format = peasycap_format; + if (!miss) + break; + } + } + peasycap_format++; + } + if (-1 == best) { + SAM("cannot do %ix... with standard mask 0x%02X\n", + width, mask); + SAM("cannot do ...x%i with standard mask 0x%02X\n", + height, mask); + SAM(" %ix%i unmatched\n", width, height); + return peasycap->format_offset; + } + } + if (!peasycap_best_format) { + SAM("MISTAKE: peasycap_best_format is NULL"); + return -EINVAL; + } + peasycap_format = peasycap_best_format; + +/*...........................................................................*/ + if (try) + return peasycap_best_format - easycap_format; +/*...........................................................................*/ + + if (false != try) { + SAM("MISTAKE: true==try where is should be false\n"); + return -EINVAL; + } + SAM("actioning: %ix%i %s\n", + peasycap_format->v4l2_format.fmt.pix.width, + peasycap_format->v4l2_format.fmt.pix.height, + &peasycap_format->name[0]); + peasycap->height = peasycap_format->v4l2_format.fmt.pix.height; + peasycap->width = peasycap_format->v4l2_format.fmt.pix.width; + peasycap->pixelformat = peasycap_format->v4l2_format.fmt.pix.pixelformat; + peasycap->format_offset = peasycap_format - easycap_format; + + + for (k = 0; k < INPUT_MANY; k++) { + if (!peasycap->inputset[k].format_offset_ok) { + peasycap->inputset[k].format_offset = + peasycap->format_offset; + } + } + if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { + peasycap->inputset[peasycap->input].format_offset = + peasycap->format_offset; + peasycap->inputset[peasycap->input].format_offset_ok = 1; + } else + JOM(8, "%i=peasycap->input\n", peasycap->input); + + + + peasycap->bytesperpixel = (0x00E0 & peasycap_format->mask) >> 5 ; + if (0x0100 & peasycap_format->mask) + peasycap->byteswaporder = true; + else + peasycap->byteswaporder = false; + if (0x0200 & peasycap_format->mask) + peasycap->skip = 5; + else + peasycap->skip = 0; + if (0x0800 & peasycap_format->mask) + peasycap->decimatepixel = true; + else + peasycap->decimatepixel = false; + if (0x1000 & peasycap_format->mask) + peasycap->offerfields = true; + else + peasycap->offerfields = false; + if (peasycap->decimatepixel) + multiplier = 2; + else + multiplier = 1; + peasycap->videofieldamount = + multiplier * peasycap->width * multiplier * peasycap->height; + peasycap->frame_buffer_used = + peasycap->bytesperpixel * peasycap->width * peasycap->height; + if (peasycap->video_isoc_streaming) { + resubmit = true; + kill_video_urbs(peasycap); + } else + resubmit = false; +/*---------------------------------------------------------------------------*/ +/* + * PAL + */ +/*---------------------------------------------------------------------------*/ + if (0 == (0x01 & peasycap_format->mask)) { + if (((720 == peasycap_format->v4l2_format.fmt.pix.width) && + (576 == peasycap_format->v4l2_format.fmt.pix.height)) || + ((360 == peasycap_format->v4l2_format.fmt.pix.width) && + (288 == peasycap_format->v4l2_format.fmt.pix.height))) { + if (set_resolution(p, 0x0000, 0x0001, 0x05A0, 0x0121)) { + SAM("ERROR: set_resolution() failed\n"); + return -EINVAL; + } + } else if ((704 == peasycap_format->v4l2_format.fmt.pix.width) && + (576 == peasycap_format->v4l2_format.fmt.pix.height)) { + if (set_resolution(p, 0x0004, 0x0001, 0x0584, 0x0121)) { + SAM("ERROR: set_resolution() failed\n"); + return -EINVAL; + } + } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) && + (480 == peasycap_format->v4l2_format.fmt.pix.height)) || + ((320 == peasycap_format->v4l2_format.fmt.pix.width) && + (240 == peasycap_format->v4l2_format.fmt.pix.height))) { + if (set_resolution(p, 0x0014, 0x0020, 0x0514, 0x0110)) { + SAM("ERROR: set_resolution() failed\n"); + return -EINVAL; + } + } else { + SAM("MISTAKE: bad format, cannot set resolution\n"); + return -EINVAL; + } +/*---------------------------------------------------------------------------*/ +/* + * NTSC + */ +/*---------------------------------------------------------------------------*/ + } else { + if (((720 == peasycap_format->v4l2_format.fmt.pix.width) && + (480 == peasycap_format->v4l2_format.fmt.pix.height)) || + ((360 == peasycap_format->v4l2_format.fmt.pix.width) && + (240 == peasycap_format->v4l2_format.fmt.pix.height))) { + if (set_resolution(p, 0x0000, 0x0003, 0x05A0, 0x00F3)) { + SAM("ERROR: set_resolution() failed\n"); + return -EINVAL; + } + } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) && + (480 == peasycap_format->v4l2_format.fmt.pix.height)) || + ((320 == peasycap_format->v4l2_format.fmt.pix.width) && + (240 == peasycap_format->v4l2_format.fmt.pix.height))) { + if (set_resolution(p, 0x0014, 0x0003, 0x0514, 0x00F3)) { + SAM("ERROR: set_resolution() failed\n"); + return -EINVAL; + } + } else { + SAM("MISTAKE: bad format, cannot set resolution\n"); + return -EINVAL; + } + } +/*---------------------------------------------------------------------------*/ + if (resubmit) + submit_video_urbs(peasycap); + + return peasycap_best_format - easycap_format; +} +/*****************************************************************************/ +int adjust_brightness(struct easycap *peasycap, int value) +{ + unsigned int mood; + int i1, k; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + i1 = 0; + while (0xFFFFFFFF != easycap_control[i1].id) { + if (V4L2_CID_BRIGHTNESS == easycap_control[i1].id) { + if ((easycap_control[i1].minimum > value) || + (easycap_control[i1].maximum < value)) + value = easycap_control[i1].default_value; + + if ((easycap_control[i1].minimum <= peasycap->brightness) && + (easycap_control[i1].maximum >= peasycap->brightness)) { + if (peasycap->brightness == value) { + SAM("unchanged brightness at 0x%02X\n", + value); + return 0; + } + } + peasycap->brightness = value; + for (k = 0; k < INPUT_MANY; k++) { + if (!peasycap->inputset[k].brightness_ok) + peasycap->inputset[k].brightness = + peasycap->brightness; + } + if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { + peasycap->inputset[peasycap->input].brightness = + peasycap->brightness; + peasycap->inputset[peasycap->input].brightness_ok = 1; + } else + JOM(8, "%i=peasycap->input\n", peasycap->input); + mood = 0x00FF & (unsigned int)peasycap->brightness; + if (!write_saa(peasycap->pusb_device, 0x0A, mood)) { + SAM("adjusting brightness to 0x%02X\n", mood); + return 0; + } else { + SAM("WARNING: failed to adjust brightness " + "to 0x%02X\n", mood); + return -ENOENT; + } + break; + } + i1++; + } + SAM("WARNING: failed to adjust brightness: control not found\n"); + return -ENOENT; +} +/*****************************************************************************/ +int adjust_contrast(struct easycap *peasycap, int value) +{ + unsigned int mood; + int i1, k; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + i1 = 0; + while (0xFFFFFFFF != easycap_control[i1].id) { + if (V4L2_CID_CONTRAST == easycap_control[i1].id) { + if ((easycap_control[i1].minimum > value) || + (easycap_control[i1].maximum < value)) + value = easycap_control[i1].default_value; + + + if ((easycap_control[i1].minimum <= peasycap->contrast) && + (easycap_control[i1].maximum >= peasycap->contrast)) { + if (peasycap->contrast == value) { + SAM("unchanged contrast at 0x%02X\n", value); + return 0; + } + } + peasycap->contrast = value; + for (k = 0; k < INPUT_MANY; k++) { + if (!peasycap->inputset[k].contrast_ok) + peasycap->inputset[k].contrast = peasycap->contrast; + } + + if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { + peasycap->inputset[peasycap->input].contrast = + peasycap->contrast; + peasycap->inputset[peasycap->input].contrast_ok = 1; + } else + JOM(8, "%i=peasycap->input\n", peasycap->input); + + mood = 0x00FF & (unsigned int) (peasycap->contrast - 128); + if (!write_saa(peasycap->pusb_device, 0x0B, mood)) { + SAM("adjusting contrast to 0x%02X\n", mood); + return 0; + } else { + SAM("WARNING: failed to adjust contrast to " + "0x%02X\n", mood); + return -ENOENT; + } + break; + } + i1++; + } + SAM("WARNING: failed to adjust contrast: control not found\n"); + return -ENOENT; +} +/*****************************************************************************/ +int adjust_saturation(struct easycap *peasycap, int value) +{ + unsigned int mood; + int i1, k; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + i1 = 0; + while (0xFFFFFFFF != easycap_control[i1].id) { + if (V4L2_CID_SATURATION == easycap_control[i1].id) { + if ((easycap_control[i1].minimum > value) || + (easycap_control[i1].maximum < value)) + value = easycap_control[i1].default_value; + + + if ((easycap_control[i1].minimum <= peasycap->saturation) && + (easycap_control[i1].maximum >= peasycap->saturation)) { + if (peasycap->saturation == value) { + SAM("unchanged saturation at 0x%02X\n", + value); + return 0; + } + } + peasycap->saturation = value; + for (k = 0; k < INPUT_MANY; k++) { + if (!peasycap->inputset[k].saturation_ok) + peasycap->inputset[k].saturation = + peasycap->saturation; + } + if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) { + peasycap->inputset[peasycap->input].saturation = + peasycap->saturation; + peasycap->inputset[peasycap->input].saturation_ok = 1; + } else + JOM(8, "%i=peasycap->input\n", peasycap->input); + mood = 0x00FF & (unsigned int) (peasycap->saturation - 128); + if (!write_saa(peasycap->pusb_device, 0x0C, mood)) { + SAM("adjusting saturation to 0x%02X\n", mood); + return 0; + } else { + SAM("WARNING: failed to adjust saturation to " + "0x%02X\n", mood); + return -ENOENT; + } + break; + } + i1++; + } + SAM("WARNING: failed to adjust saturation: control not found\n"); + return -ENOENT; +} +/*****************************************************************************/ +int adjust_hue(struct easycap *peasycap, int value) +{ + unsigned int mood; + int i1, i2, k; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + i1 = 0; + while (0xFFFFFFFF != easycap_control[i1].id) { + if (V4L2_CID_HUE == easycap_control[i1].id) { + if ((easycap_control[i1].minimum > value) || + (easycap_control[i1].maximum < value)) + value = easycap_control[i1].default_value; + + if ((easycap_control[i1].minimum <= peasycap->hue) && + (easycap_control[i1].maximum >= peasycap->hue)) { + if (peasycap->hue == value) { + SAM("unchanged hue at 0x%02X\n", value); + return 0; + } + } + peasycap->hue = value; + for (k = 0; k < INPUT_MANY; k++) { + if (!peasycap->inputset[k].hue_ok) + peasycap->inputset[k].hue = peasycap->hue; + } + if (0 <= peasycap->input && INPUT_MANY > peasycap->input) { + peasycap->inputset[peasycap->input].hue = peasycap->hue; + peasycap->inputset[peasycap->input].hue_ok = 1; + } else + JOM(8, "%i=peasycap->input\n", peasycap->input); + i2 = peasycap->hue - 128; + mood = 0x00FF & ((int) i2); + if (!write_saa(peasycap->pusb_device, 0x0D, mood)) { + SAM("adjusting hue to 0x%02X\n", mood); + return 0; + } else { + SAM("WARNING: failed to adjust hue to 0x%02X\n", mood); + return -ENOENT; + } + break; + } + i1++; + } + SAM("WARNING: failed to adjust hue: control not found\n"); + return -ENOENT; +} +/*****************************************************************************/ +int adjust_volume(struct easycap *peasycap, int value) +{ + s8 mood; + int i1; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + i1 = 0; + while (0xFFFFFFFF != easycap_control[i1].id) { + if (V4L2_CID_AUDIO_VOLUME == easycap_control[i1].id) { + if ((easycap_control[i1].minimum > value) || + (easycap_control[i1].maximum < value)) + value = easycap_control[i1].default_value; + + if ((easycap_control[i1].minimum <= peasycap->volume) && + (easycap_control[i1].maximum >= peasycap->volume)) { + if (peasycap->volume == value) { + SAM("unchanged volume at 0x%02X\n", value); + return 0; + } + } + peasycap->volume = value; + mood = (16 > peasycap->volume) ? 16 : + ((31 < peasycap->volume) ? 31 : + (s8) peasycap->volume); + if (!audio_gainset(peasycap->pusb_device, mood)) { + SAM("adjusting volume to 0x%02X\n", mood); + return 0; + } else { + SAM("WARNING: failed to adjust volume to " + "0x%2X\n", mood); + return -ENOENT; + } + break; + } + i1++; + } + SAM("WARNING: failed to adjust volume: control not found\n"); + return -ENOENT; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * AN ALTERNATIVE METHOD OF MUTING MIGHT SEEM TO BE: + * usb_set_interface(peasycap->pusb_device, + * peasycap->audio_interface, + * peasycap->audio_altsetting_off); + * HOWEVER, AFTER THIS COMMAND IS ISSUED ALL SUBSEQUENT URBS RECEIVE STATUS + * -ESHUTDOWN. THE HANDLER ROUTINE easyxxx_complete() DECLINES TO RESUBMIT + * THE URB AND THE PIPELINE COLLAPSES IRRETRIEVABLY. BEWARE. + */ +/*---------------------------------------------------------------------------*/ +static int adjust_mute(struct easycap *peasycap, int value) +{ + int i1; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + i1 = 0; + while (0xFFFFFFFF != easycap_control[i1].id) { + if (V4L2_CID_AUDIO_MUTE == easycap_control[i1].id) { + peasycap->mute = value; + switch (peasycap->mute) { + case 1: { + peasycap->audio_idle = 1; + SAM("adjusting mute: %i=peasycap->audio_idle\n", + peasycap->audio_idle); + return 0; + } + default: { + peasycap->audio_idle = 0; + SAM("adjusting mute: %i=peasycap->audio_idle\n", + peasycap->audio_idle); + return 0; + } + } + break; + } + i1++; + } + SAM("WARNING: failed to adjust mute: control not found\n"); + return -ENOENT; +} +/*---------------------------------------------------------------------------*/ +long easycap_unlocked_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct easycap *peasycap; + struct usb_device *p; + int kd; + + if (!file) { + SAY("ERROR: file is NULL\n"); + return -ERESTARTSYS; + } + peasycap = file->private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -1; + } + p = peasycap->pusb_device; + if (!p) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + kd = isdongle(peasycap); + if (0 <= kd && DONGLE_MANY > kd) { + if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { + SAY("ERROR: cannot lock " + "easycapdc60_dongle[%i].mutex_video\n", kd); + return -ERESTARTSYS; + } + JOM(4, "locked easycapdc60_dongle[%i].mutex_video\n", kd); +/*---------------------------------------------------------------------------*/ +/* + * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap, + * IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL. + * IF NECESSARY, BAIL OUT. + */ +/*---------------------------------------------------------------------------*/ + if (kd != isdongle(peasycap)) + return -ERESTARTSYS; + if (!file) { + SAY("ERROR: file is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ERESTARTSYS; + } + peasycap = file->private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ERESTARTSYS; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ERESTARTSYS; + } + } else { +/*---------------------------------------------------------------------------*/ +/* + * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE + * ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT. + */ +/*---------------------------------------------------------------------------*/ + return -ERESTARTSYS; + } +/*---------------------------------------------------------------------------*/ + switch (cmd) { + case VIDIOC_QUERYCAP: { + struct v4l2_capability v4l2_capability; + char version[16], *p1, *p2; + int i, rc, k[3]; + long lng; + + JOM(8, "VIDIOC_QUERYCAP\n"); + + if (16 <= strlen(EASYCAP_DRIVER_VERSION)) { + SAM("ERROR: bad driver version string\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + strcpy(&version[0], EASYCAP_DRIVER_VERSION); + for (i = 0; i < 3; i++) + k[i] = 0; + p2 = &version[0]; + i = 0; + while (*p2) { + p1 = p2; + while (*p2 && ('.' != *p2)) + p2++; + if (*p2) + *p2++ = 0; + if (3 > i) { + rc = (int) strict_strtol(p1, 10, &lng); + if (rc) { + SAM("ERROR: %i=strict_strtol(%s,.,,)\n", + rc, p1); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + k[i] = (int)lng; + } + i++; + } + + memset(&v4l2_capability, 0, sizeof(struct v4l2_capability)); + strlcpy(&v4l2_capability.driver[0], + "easycap", sizeof(v4l2_capability.driver)); + + v4l2_capability.capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_AUDIO | + V4L2_CAP_READWRITE; + + v4l2_capability.version = KERNEL_VERSION(k[0], k[1], k[2]); + JOM(8, "v4l2_capability.version=(%i,%i,%i)\n", k[0], k[1], k[2]); + + strlcpy(&v4l2_capability.card[0], + "EasyCAP DC60", sizeof(v4l2_capability.card)); + + if (usb_make_path(peasycap->pusb_device, + &v4l2_capability.bus_info[0], + sizeof(v4l2_capability.bus_info)) < 0) { + + strlcpy(&v4l2_capability.bus_info[0], "EasyCAP bus_info", + sizeof(v4l2_capability.bus_info)); + JOM(8, "%s=v4l2_capability.bus_info\n", + &v4l2_capability.bus_info[0]); + } + if (copy_to_user((void __user *)arg, &v4l2_capability, + sizeof(struct v4l2_capability))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_ENUMINPUT: { + struct v4l2_input v4l2_input; + u32 index; + + JOM(8, "VIDIOC_ENUMINPUT\n"); + + if (copy_from_user(&v4l2_input, (void __user *)arg, + sizeof(struct v4l2_input))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + index = v4l2_input.index; + memset(&v4l2_input, 0, sizeof(struct v4l2_input)); + + switch (index) { + case 0: { + v4l2_input.index = index; + strcpy(&v4l2_input.name[0], "CVBS0"); + v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; + v4l2_input.audioset = 0x01; + v4l2_input.tuner = 0; + v4l2_input.std = V4L2_STD_PAL | + V4L2_STD_SECAM | + V4L2_STD_NTSC ; + v4l2_input.status = 0; + JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); + break; + } + case 1: { + v4l2_input.index = index; + strcpy(&v4l2_input.name[0], "CVBS1"); + v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; + v4l2_input.audioset = 0x01; + v4l2_input.tuner = 0; + v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | + V4L2_STD_NTSC; + v4l2_input.status = 0; + JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); + break; + } + case 2: { + v4l2_input.index = index; + strcpy(&v4l2_input.name[0], "CVBS2"); + v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; + v4l2_input.audioset = 0x01; + v4l2_input.tuner = 0; + v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | + V4L2_STD_NTSC ; + v4l2_input.status = 0; + JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); + break; + } + case 3: { + v4l2_input.index = index; + strcpy(&v4l2_input.name[0], "CVBS3"); + v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; + v4l2_input.audioset = 0x01; + v4l2_input.tuner = 0; + v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | + V4L2_STD_NTSC ; + v4l2_input.status = 0; + JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); + break; + } + case 4: { + v4l2_input.index = index; + strcpy(&v4l2_input.name[0], "CVBS4"); + v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; + v4l2_input.audioset = 0x01; + v4l2_input.tuner = 0; + v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | + V4L2_STD_NTSC ; + v4l2_input.status = 0; + JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); + break; + } + case 5: { + v4l2_input.index = index; + strcpy(&v4l2_input.name[0], "S-VIDEO"); + v4l2_input.type = V4L2_INPUT_TYPE_CAMERA; + v4l2_input.audioset = 0x01; + v4l2_input.tuner = 0; + v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM | + V4L2_STD_NTSC ; + v4l2_input.status = 0; + JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]); + break; + } + default: { + JOM(8, "%i=index: exhausts inputs\n", index); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + } + + if (copy_to_user((void __user *)arg, &v4l2_input, + sizeof(struct v4l2_input))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_INPUT: { + u32 index; + + JOM(8, "VIDIOC_G_INPUT\n"); + index = (u32)peasycap->input; + JOM(8, "user is told: %i\n", index); + if (copy_to_user((void __user *)arg, &index, sizeof(u32))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_S_INPUT: + { + u32 index; + int rc; + + JOM(8, "VIDIOC_S_INPUT\n"); + + if (0 != copy_from_user(&index, (void __user *)arg, sizeof(u32))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + JOM(8, "user requests input %i\n", index); + + if ((int)index == peasycap->input) { + SAM("requested input already in effect\n"); + break; + } + + if ((0 > index) || (INPUT_MANY <= index)) { + JOM(8, "ERROR: bad requested input: %i\n", index); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + + rc = newinput(peasycap, (int)index); + if (0 == rc) { + JOM(8, "newinput(.,%i) OK\n", (int)index); + } else { + SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_ENUMAUDIO: { + JOM(8, "VIDIOC_ENUMAUDIO\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_ENUMAUDOUT: { + struct v4l2_audioout v4l2_audioout; + + JOM(8, "VIDIOC_ENUMAUDOUT\n"); + + if (copy_from_user(&v4l2_audioout, (void __user *)arg, + sizeof(struct v4l2_audioout))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + if (0 != v4l2_audioout.index) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout)); + v4l2_audioout.index = 0; + strcpy(&v4l2_audioout.name[0], "Soundtrack"); + + if (copy_to_user((void __user *)arg, &v4l2_audioout, + sizeof(struct v4l2_audioout))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_QUERYCTRL: { + int i1; + struct v4l2_queryctrl v4l2_queryctrl; + + JOM(8, "VIDIOC_QUERYCTRL\n"); + + if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg, + sizeof(struct v4l2_queryctrl))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + i1 = 0; + while (0xFFFFFFFF != easycap_control[i1].id) { + if (easycap_control[i1].id == v4l2_queryctrl.id) { + JOM(8, "VIDIOC_QUERYCTRL %s=easycap_control[%i]" + ".name\n", &easycap_control[i1].name[0], i1); + memcpy(&v4l2_queryctrl, &easycap_control[i1], + sizeof(struct v4l2_queryctrl)); + break; + } + i1++; + } + if (0xFFFFFFFF == easycap_control[i1].id) { + JOM(8, "%i=index: exhausts controls\n", i1); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + if (copy_to_user((void __user *)arg, &v4l2_queryctrl, + sizeof(struct v4l2_queryctrl))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_QUERYMENU: { + JOM(8, "VIDIOC_QUERYMENU unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_CTRL: { + struct v4l2_control *pv4l2_control; + + JOM(8, "VIDIOC_G_CTRL\n"); + pv4l2_control = memdup_user((void __user *)arg, + sizeof(struct v4l2_control)); + if (IS_ERR(pv4l2_control)) { + SAM("ERROR: copy from user failed\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return PTR_ERR(pv4l2_control); + } + + switch (pv4l2_control->id) { + case V4L2_CID_BRIGHTNESS: { + pv4l2_control->value = peasycap->brightness; + JOM(8, "user enquires brightness: %i\n", pv4l2_control->value); + break; + } + case V4L2_CID_CONTRAST: { + pv4l2_control->value = peasycap->contrast; + JOM(8, "user enquires contrast: %i\n", pv4l2_control->value); + break; + } + case V4L2_CID_SATURATION: { + pv4l2_control->value = peasycap->saturation; + JOM(8, "user enquires saturation: %i\n", pv4l2_control->value); + break; + } + case V4L2_CID_HUE: { + pv4l2_control->value = peasycap->hue; + JOM(8, "user enquires hue: %i\n", pv4l2_control->value); + break; + } + case V4L2_CID_AUDIO_VOLUME: { + pv4l2_control->value = peasycap->volume; + JOM(8, "user enquires volume: %i\n", pv4l2_control->value); + break; + } + case V4L2_CID_AUDIO_MUTE: { + if (1 == peasycap->mute) + pv4l2_control->value = true; + else + pv4l2_control->value = false; + JOM(8, "user enquires mute: %i\n", pv4l2_control->value); + break; + } + default: { + SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", + pv4l2_control->id); + kfree(pv4l2_control); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + } + if (copy_to_user((void __user *)arg, pv4l2_control, + sizeof(struct v4l2_control))) { + kfree(pv4l2_control); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + kfree(pv4l2_control); + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_S_CTRL: { + struct v4l2_control v4l2_control; + + JOM(8, "VIDIOC_S_CTRL\n"); + + if (0 != copy_from_user(&v4l2_control, (void __user *)arg, + sizeof(struct v4l2_control))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + switch (v4l2_control.id) { + case V4L2_CID_BRIGHTNESS: { + JOM(8, "user requests brightness %i\n", v4l2_control.value); + if (0 != adjust_brightness(peasycap, v4l2_control.value)) + ; + break; + } + case V4L2_CID_CONTRAST: { + JOM(8, "user requests contrast %i\n", v4l2_control.value); + if (0 != adjust_contrast(peasycap, v4l2_control.value)) + ; + break; + } + case V4L2_CID_SATURATION: { + JOM(8, "user requests saturation %i\n", v4l2_control.value); + if (0 != adjust_saturation(peasycap, v4l2_control.value)) + ; + break; + } + case V4L2_CID_HUE: { + JOM(8, "user requests hue %i\n", v4l2_control.value); + if (0 != adjust_hue(peasycap, v4l2_control.value)) + ; + break; + } + case V4L2_CID_AUDIO_VOLUME: { + JOM(8, "user requests volume %i\n", v4l2_control.value); + if (0 != adjust_volume(peasycap, v4l2_control.value)) + ; + break; + } + case V4L2_CID_AUDIO_MUTE: { + int mute; + + JOM(8, "user requests mute %i\n", v4l2_control.value); + if (v4l2_control.value) + mute = 1; + else + mute = 0; + + if (0 != adjust_mute(peasycap, mute)) + SAM("WARNING: failed to adjust mute to %i\n", mute); + break; + } + default: { + SAM("ERROR: unknown V4L2 control: 0x%08X=id\n", + v4l2_control.id); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_S_EXT_CTRLS: { + JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_ENUM_FMT: { + u32 index; + struct v4l2_fmtdesc v4l2_fmtdesc; + + JOM(8, "VIDIOC_ENUM_FMT\n"); + + if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg, + sizeof(struct v4l2_fmtdesc))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + index = v4l2_fmtdesc.index; + memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc)); + + v4l2_fmtdesc.index = index; + v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + switch (index) { + case 0: { + v4l2_fmtdesc.flags = 0; + strcpy(&v4l2_fmtdesc.description[0], "uyvy"); + v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_UYVY; + JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); + break; + } + case 1: { + v4l2_fmtdesc.flags = 0; + strcpy(&v4l2_fmtdesc.description[0], "yuy2"); + v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV; + JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); + break; + } + case 2: { + v4l2_fmtdesc.flags = 0; + strcpy(&v4l2_fmtdesc.description[0], "rgb24"); + v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB24; + JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); + break; + } + case 3: { + v4l2_fmtdesc.flags = 0; + strcpy(&v4l2_fmtdesc.description[0], "rgb32"); + v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB32; + JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); + break; + } + case 4: { + v4l2_fmtdesc.flags = 0; + strcpy(&v4l2_fmtdesc.description[0], "bgr24"); + v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR24; + JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); + break; + } + case 5: { + v4l2_fmtdesc.flags = 0; + strcpy(&v4l2_fmtdesc.description[0], "bgr32"); + v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR32; + JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]); + break; + } + default: { + JOM(8, "%i=index: exhausts formats\n", index); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + } + if (copy_to_user((void __user *)arg, &v4l2_fmtdesc, + sizeof(struct v4l2_fmtdesc))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* + * THE RESPONSE TO VIDIOC_ENUM_FRAMESIZES MUST BE CONDITIONED ON THE + * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE. + */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_ENUM_FRAMESIZES: { + u32 index; + struct v4l2_frmsizeenum v4l2_frmsizeenum; + + JOM(8, "VIDIOC_ENUM_FRAMESIZES\n"); + + if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg, + sizeof(struct v4l2_frmsizeenum))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + index = v4l2_frmsizeenum.index; + + v4l2_frmsizeenum.type = (u32) V4L2_FRMSIZE_TYPE_DISCRETE; + + if (peasycap->ntsc) { + switch (index) { + case 0: { + v4l2_frmsizeenum.discrete.width = 640; + v4l2_frmsizeenum.discrete.height = 480; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + case 1: { + v4l2_frmsizeenum.discrete.width = 320; + v4l2_frmsizeenum.discrete.height = 240; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + case 2: { + v4l2_frmsizeenum.discrete.width = 720; + v4l2_frmsizeenum.discrete.height = 480; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + case 3: { + v4l2_frmsizeenum.discrete.width = 360; + v4l2_frmsizeenum.discrete.height = 240; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + default: { + JOM(8, "%i=index: exhausts framesizes\n", index); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + } + } else { + switch (index) { + case 0: { + v4l2_frmsizeenum.discrete.width = 640; + v4l2_frmsizeenum.discrete.height = 480; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + case 1: { + v4l2_frmsizeenum.discrete.width = 320; + v4l2_frmsizeenum.discrete.height = 240; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + case 2: { + v4l2_frmsizeenum.discrete.width = 704; + v4l2_frmsizeenum.discrete.height = 576; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + case 3: { + v4l2_frmsizeenum.discrete.width = 720; + v4l2_frmsizeenum.discrete.height = 576; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + case 4: { + v4l2_frmsizeenum.discrete.width = 360; + v4l2_frmsizeenum.discrete.height = 288; + JOM(8, "%i=index: %ix%i\n", index, + (int)(v4l2_frmsizeenum. + discrete.width), + (int)(v4l2_frmsizeenum. + discrete.height)); + break; + } + default: { + JOM(8, "%i=index: exhausts framesizes\n", index); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + } + } + if (copy_to_user((void __user *)arg, &v4l2_frmsizeenum, + sizeof(struct v4l2_frmsizeenum))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* + * THE RESPONSE TO VIDIOC_ENUM_FRAMEINTERVALS MUST BE CONDITIONED ON THE + * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE. + */ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_ENUM_FRAMEINTERVALS: { + u32 index; + int denominator; + struct v4l2_frmivalenum v4l2_frmivalenum; + + JOM(8, "VIDIOC_ENUM_FRAMEINTERVALS\n"); + + if (peasycap->fps) + denominator = peasycap->fps; + else { + if (peasycap->ntsc) + denominator = 30; + else + denominator = 25; + } + + if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg, + sizeof(struct v4l2_frmivalenum))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + index = v4l2_frmivalenum.index; + + v4l2_frmivalenum.type = (u32) V4L2_FRMIVAL_TYPE_DISCRETE; + + switch (index) { + case 0: { + v4l2_frmivalenum.discrete.numerator = 1; + v4l2_frmivalenum.discrete.denominator = denominator; + JOM(8, "%i=index: %i/%i\n", index, + (int)(v4l2_frmivalenum.discrete.numerator), + (int)(v4l2_frmivalenum.discrete.denominator)); + break; + } + case 1: { + v4l2_frmivalenum.discrete.numerator = 1; + v4l2_frmivalenum.discrete.denominator = denominator/5; + JOM(8, "%i=index: %i/%i\n", index, + (int)(v4l2_frmivalenum.discrete.numerator), + (int)(v4l2_frmivalenum.discrete.denominator)); + break; + } + default: { + JOM(8, "%i=index: exhausts frameintervals\n", index); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + } + if (copy_to_user((void __user *)arg, &v4l2_frmivalenum, + sizeof(struct v4l2_frmivalenum))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_FMT: { + struct v4l2_format *pv4l2_format; + struct v4l2_pix_format *pv4l2_pix_format; + + JOM(8, "VIDIOC_G_FMT\n"); + pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL); + if (!pv4l2_format) { + SAM("ERROR: out of memory\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ENOMEM; + } + pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL); + if (!pv4l2_pix_format) { + SAM("ERROR: out of memory\n"); + kfree(pv4l2_format); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ENOMEM; + } + if (0 != copy_from_user(pv4l2_format, (void __user *)arg, + sizeof(struct v4l2_format))) { + kfree(pv4l2_format); + kfree(pv4l2_pix_format); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + kfree(pv4l2_format); + kfree(pv4l2_pix_format); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + + memset(pv4l2_pix_format, 0, sizeof(struct v4l2_pix_format)); + pv4l2_format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + memcpy(&pv4l2_format->fmt.pix, + &easycap_format[peasycap->format_offset] + .v4l2_format.fmt.pix, sizeof(struct v4l2_pix_format)); + JOM(8, "user is told: %s\n", + &easycap_format[peasycap->format_offset].name[0]); + + if (copy_to_user((void __user *)arg, pv4l2_format, + sizeof(struct v4l2_format))) { + kfree(pv4l2_format); + kfree(pv4l2_pix_format); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + kfree(pv4l2_format); + kfree(pv4l2_pix_format); + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_TRY_FMT: + case VIDIOC_S_FMT: { + struct v4l2_format v4l2_format; + struct v4l2_pix_format v4l2_pix_format; + bool try; + int best_format; + + if (VIDIOC_TRY_FMT == cmd) { + JOM(8, "VIDIOC_TRY_FMT\n"); + try = true; + } else { + JOM(8, "VIDIOC_S_FMT\n"); + try = false; + } + + if (0 != copy_from_user(&v4l2_format, (void __user *)arg, + sizeof(struct v4l2_format))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + best_format = adjust_format(peasycap, + v4l2_format.fmt.pix.width, + v4l2_format.fmt.pix.height, + v4l2_format.fmt.pix.pixelformat, + v4l2_format.fmt.pix.field, + try); + if (0 > best_format) { + if (-EBUSY == best_format) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EBUSY; + } + JOM(8, "WARNING: adjust_format() returned %i\n", best_format); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ENOENT; + } +/*...........................................................................*/ + memset(&v4l2_pix_format, 0, sizeof(struct v4l2_pix_format)); + v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + memcpy(&(v4l2_format.fmt.pix), + &(easycap_format[best_format].v4l2_format.fmt.pix), + sizeof(v4l2_pix_format)); + JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]); + + if (copy_to_user((void __user *)arg, &v4l2_format, + sizeof(struct v4l2_format))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_CROPCAP: { + struct v4l2_cropcap v4l2_cropcap; + + JOM(8, "VIDIOC_CROPCAP\n"); + + if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg, + sizeof(struct v4l2_cropcap))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n"); + + memset(&v4l2_cropcap, 0, sizeof(struct v4l2_cropcap)); + v4l2_cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_cropcap.bounds.left = 0; + v4l2_cropcap.bounds.top = 0; + v4l2_cropcap.bounds.width = peasycap->width; + v4l2_cropcap.bounds.height = peasycap->height; + v4l2_cropcap.defrect.left = 0; + v4l2_cropcap.defrect.top = 0; + v4l2_cropcap.defrect.width = peasycap->width; + v4l2_cropcap.defrect.height = peasycap->height; + v4l2_cropcap.pixelaspect.numerator = 1; + v4l2_cropcap.pixelaspect.denominator = 1; + + JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height); + + if (copy_to_user((void __user *)arg, &v4l2_cropcap, + sizeof(struct v4l2_cropcap))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_CROP: + case VIDIOC_S_CROP: { + JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_QUERYSTD: { + JOM(8, "VIDIOC_QUERYSTD: " + "EasyCAP is incapable of detecting standard\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + break; + } + /*-------------------------------------------------------------------*/ + /* + * THE MANIPULATIONS INVOLVING last0,last1,last2,last3 + * CONSTITUTE A WORKAROUND * FOR WHAT APPEARS TO BE + * A BUG IN 64-BIT mplayer. + * NOT NEEDED, BUT HOPEFULLY HARMLESS, FOR 32-BIT mplayer. + */ + /*------------------------------------------------------------------*/ + case VIDIOC_ENUMSTD: { + int last0 = -1, last1 = -1, last2 = -1, last3 = -1; + struct v4l2_standard v4l2_standard; + u32 index; + struct easycap_standard const *peasycap_standard; + + JOM(8, "VIDIOC_ENUMSTD\n"); + + if (0 != copy_from_user(&v4l2_standard, (void __user *)arg, + sizeof(struct v4l2_standard))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + index = v4l2_standard.index; + + last3 = last2; + last2 = last1; + last1 = last0; + last0 = index; + if ((index == last3) && (index == last2) && + (index == last1) && (index == last0)) { + index++; + last3 = last2; + last2 = last1; + last1 = last0; + last0 = index; + } + + memset(&v4l2_standard, 0, sizeof(struct v4l2_standard)); + + peasycap_standard = &easycap_standard[0]; + while (0xFFFF != peasycap_standard->mask) { + if ((int)(peasycap_standard - &easycap_standard[0]) == index) + break; + peasycap_standard++; + } + if (0xFFFF == peasycap_standard->mask) { + JOM(8, "%i=index: exhausts standards\n", index); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + JOM(8, "%i=index: %s\n", index, + &(peasycap_standard->v4l2_standard.name[0])); + memcpy(&v4l2_standard, &(peasycap_standard->v4l2_standard), + sizeof(struct v4l2_standard)); + + v4l2_standard.index = index; + + if (copy_to_user((void __user *)arg, &v4l2_standard, + sizeof(struct v4l2_standard))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_STD: { + v4l2_std_id std_id; + struct easycap_standard const *peasycap_standard; + + JOM(8, "VIDIOC_G_STD\n"); + + if (0 > peasycap->standard_offset) { + JOM(8, "%i=peasycap->standard_offset\n", + peasycap->standard_offset); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EBUSY; + } + + if (0 != copy_from_user(&std_id, (void __user *)arg, + sizeof(v4l2_std_id))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + peasycap_standard = &easycap_standard[peasycap->standard_offset]; + std_id = peasycap_standard->v4l2_standard.id; + + JOM(8, "user is told: %s\n", + &peasycap_standard->v4l2_standard.name[0]); + + if (copy_to_user((void __user *)arg, &std_id, + sizeof(v4l2_std_id))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_S_STD: { + v4l2_std_id std_id; + int rc; + + JOM(8, "VIDIOC_S_STD\n"); + + if (0 != copy_from_user(&std_id, (void __user *)arg, + sizeof(v4l2_std_id))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + JOM(8, "User requests standard: 0x%08X%08X\n", + (int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32), + (int)(std_id & ((v4l2_std_id)0xFFFFFFFF))); + + rc = adjust_standard(peasycap, std_id); + if (0 > rc) { + JOM(8, "WARNING: adjust_standard() returned %i\n", rc); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ENOENT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_REQBUFS: { + int nbuffers; + struct v4l2_requestbuffers v4l2_requestbuffers; + + JOM(8, "VIDIOC_REQBUFS\n"); + + if (0 != copy_from_user(&v4l2_requestbuffers, + (void __user *)arg, + sizeof(struct v4l2_requestbuffers))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + nbuffers = v4l2_requestbuffers.count; + JOM(8, " User requests %i buffers ...\n", nbuffers); + if (nbuffers < 2) + nbuffers = 2; + if (nbuffers > FRAME_BUFFER_MANY) + nbuffers = FRAME_BUFFER_MANY; + if (v4l2_requestbuffers.count == nbuffers) { + JOM(8, " ... agree to %i buffers\n", + nbuffers); + } else { + JOM(8, " ... insist on %i buffers\n", + nbuffers); + v4l2_requestbuffers.count = nbuffers; + } + peasycap->frame_buffer_many = nbuffers; + + if (copy_to_user((void __user *)arg, &v4l2_requestbuffers, + sizeof(struct v4l2_requestbuffers))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_QUERYBUF: { + u32 index; + struct v4l2_buffer v4l2_buffer; + + JOM(8, "VIDIOC_QUERYBUF\n"); + + if (peasycap->video_eof) { + JOM(8, "returning -EIO because %i=video_eof\n", + peasycap->video_eof); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EIO; + } + + if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, + sizeof(struct v4l2_buffer))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + index = v4l2_buffer.index; + if (index < 0 || index >= peasycap->frame_buffer_many) + return -EINVAL; + memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer)); + v4l2_buffer.index = index; + v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_buffer.bytesused = peasycap->frame_buffer_used; + v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | + peasycap->done[index] | + peasycap->queued[index]; + v4l2_buffer.field = V4L2_FIELD_NONE; + v4l2_buffer.memory = V4L2_MEMORY_MMAP; + v4l2_buffer.m.offset = index * FRAME_BUFFER_SIZE; + v4l2_buffer.length = FRAME_BUFFER_SIZE; + + JOM(16, " %10i=index\n", v4l2_buffer.index); + JOM(16, " 0x%08X=type\n", v4l2_buffer.type); + JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused); + JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags); + JOM(16, " %10i=field\n", v4l2_buffer.field); + JOM(16, " %10li=timestamp.tv_usec\n", + (long)v4l2_buffer.timestamp.tv_usec); + JOM(16, " %10i=sequence\n", v4l2_buffer.sequence); + JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory); + JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset); + JOM(16, " %10i=length\n", v4l2_buffer.length); + + if (copy_to_user((void __user *)arg, &v4l2_buffer, + sizeof(struct v4l2_buffer))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_QBUF: { + struct v4l2_buffer v4l2_buffer; + + JOM(8, "VIDIOC_QBUF\n"); + + if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, + sizeof(struct v4l2_buffer))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + if (v4l2_buffer.index < 0 || + v4l2_buffer.index >= peasycap->frame_buffer_many) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED; + + peasycap->done[v4l2_buffer.index] = 0; + peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED; + + if (copy_to_user((void __user *)arg, &v4l2_buffer, + sizeof(struct v4l2_buffer))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + JOM(8, "..... user queueing frame buffer %i\n", + (int)v4l2_buffer.index); + + peasycap->frame_lock = 0; + + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_DQBUF: + { + struct timeval timeval, timeval2; + int i, j; + struct v4l2_buffer v4l2_buffer; + int rcdq; + u16 input; + + JOM(8, "VIDIOC_DQBUF\n"); + + if ((peasycap->video_idle) || (peasycap->video_eof)) { + JOM(8, "returning -EIO because " + "%i=video_idle %i=video_eof\n", + peasycap->video_idle, peasycap->video_eof); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EIO; + } + + if (copy_from_user(&v4l2_buffer, (void __user *)arg, + sizeof(struct v4l2_buffer))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + + if (peasycap->offerfields) { + /*---------------------------------------------------*/ + /* + * IN ITS 50 "fps" MODE tvtime SEEMS ALWAYS TO REQUEST + * V4L2_FIELD_BOTTOM + */ + /*---------------------------------------------------*/ + if (V4L2_FIELD_TOP == v4l2_buffer.field) + JOM(8, "user wants V4L2_FIELD_TOP\n"); + else if (V4L2_FIELD_BOTTOM == v4l2_buffer.field) + JOM(8, "user wants V4L2_FIELD_BOTTOM\n"); + else if (V4L2_FIELD_ANY == v4l2_buffer.field) + JOM(8, "user wants V4L2_FIELD_ANY\n"); + else + JOM(8, "user wants V4L2_FIELD_...UNKNOWN: %i\n", + v4l2_buffer.field); + } + + if (!peasycap->video_isoc_streaming) { + JOM(16, "returning -EIO because video urbs not streaming\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EIO; + } + /*-------------------------------------------------------------------*/ + /* + * IF THE USER HAS PREVIOUSLY CALLED easycap_poll(), + * AS DETERMINED BY FINDING + * THE FLAG peasycap->polled SET, THERE MUST BE + * NO FURTHER WAIT HERE. IN THIS + * CASE, JUST CHOOSE THE FRAME INDICATED BY peasycap->frame_read + */ + /*-------------------------------------------------------------------*/ + + if (!peasycap->polled) { + do { + rcdq = easycap_dqbuf(peasycap, 0); + if (-EIO == rcdq) { + JOM(8, "returning -EIO because " + "dqbuf() returned -EIO\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EIO; + } + } while (0 != rcdq); + } else { + if (peasycap->video_eof) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EIO; + } + } + if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) { + JOM(8, "V4L2_BUF_FLAG_DONE != 0x%08X\n", + peasycap->done[peasycap->frame_read]); + } + peasycap->polled = 0; + + if (!(peasycap->isequence % 10)) { + for (i = 0; i < 179; i++) + peasycap->merit[i] = peasycap->merit[i+1]; + peasycap->merit[179] = merit_saa(peasycap->pusb_device); + j = 0; + for (i = 0; i < 180; i++) + j += peasycap->merit[i]; + if (90 < j) { + SAM("easycap driver shutting down " + "on condition blue\n"); + peasycap->video_eof = 1; + peasycap->audio_eof = 1; + } + } + + v4l2_buffer.index = peasycap->frame_read; + v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + v4l2_buffer.bytesused = peasycap->frame_buffer_used; + v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE; + if (peasycap->offerfields) + v4l2_buffer.field = V4L2_FIELD_BOTTOM; + else + v4l2_buffer.field = V4L2_FIELD_NONE; + do_gettimeofday(&timeval); + timeval2 = timeval; + + v4l2_buffer.timestamp = timeval2; + v4l2_buffer.sequence = peasycap->isequence++; + v4l2_buffer.memory = V4L2_MEMORY_MMAP; + v4l2_buffer.m.offset = v4l2_buffer.index * FRAME_BUFFER_SIZE; + v4l2_buffer.length = FRAME_BUFFER_SIZE; + + JOM(16, " %10i=index\n", v4l2_buffer.index); + JOM(16, " 0x%08X=type\n", v4l2_buffer.type); + JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused); + JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags); + JOM(16, " %10i=field\n", v4l2_buffer.field); + JOM(16, " %10li=timestamp.tv_sec\n", + (long)v4l2_buffer.timestamp.tv_sec); + JOM(16, " %10li=timestamp.tv_usec\n", + (long)v4l2_buffer.timestamp.tv_usec); + JOM(16, " %10i=sequence\n", v4l2_buffer.sequence); + JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory); + JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset); + JOM(16, " %10i=length\n", v4l2_buffer.length); + + if (copy_to_user((void __user *)arg, &v4l2_buffer, + sizeof(struct v4l2_buffer))) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + input = peasycap->frame_buffer[peasycap->frame_read][0].input; + if (0x08 & input) { + JOM(8, "user is offered frame buffer %i, input %i\n", + peasycap->frame_read, (0x07 & input)); + } else { + JOM(8, "user is offered frame buffer %i\n", + peasycap->frame_read); + } + peasycap->frame_lock = 1; + JOM(8, "%i=peasycap->frame_fill\n", peasycap->frame_fill); + if (peasycap->frame_read == peasycap->frame_fill) { + if (peasycap->frame_lock) { + JOM(8, "WORRY: filling frame buffer " + "while offered to user\n"); + } + } + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_STREAMON: { + int i; + + JOM(8, "VIDIOC_STREAMON\n"); + + peasycap->isequence = 0; + for (i = 0; i < 180; i++) + peasycap->merit[i] = 0; + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + submit_video_urbs(peasycap); + peasycap->video_idle = 0; + peasycap->audio_idle = 0; + peasycap->video_eof = 0; + peasycap->audio_eof = 0; + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_STREAMOFF: { + JOM(8, "VIDIOC_STREAMOFF\n"); + + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + + peasycap->video_idle = 1; + peasycap->audio_idle = 1; +/*---------------------------------------------------------------------------*/ +/* + * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO THE STREAMOFF COMMAND + * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE. + */ +/*---------------------------------------------------------------------------*/ + JOM(8, "calling wake_up on wq_video and wq_audio\n"); + wake_up_interruptible(&(peasycap->wq_video)); + if (peasycap->psubstream) + snd_pcm_period_elapsed(peasycap->psubstream); + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_PARM: { + struct v4l2_streamparm *pv4l2_streamparm; + + JOM(8, "VIDIOC_G_PARM\n"); + pv4l2_streamparm = memdup_user((void __user *)arg, + sizeof(struct v4l2_streamparm)); + if (IS_ERR(pv4l2_streamparm)) { + SAM("ERROR: copy from user failed\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return PTR_ERR(pv4l2_streamparm); + } + + if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + kfree(pv4l2_streamparm); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + pv4l2_streamparm->parm.capture.capability = 0; + pv4l2_streamparm->parm.capture.capturemode = 0; + pv4l2_streamparm->parm.capture.timeperframe.numerator = 1; + + if (peasycap->fps) { + pv4l2_streamparm->parm.capture.timeperframe. + denominator = peasycap->fps; + } else { + if (peasycap->ntsc) { + pv4l2_streamparm->parm.capture.timeperframe. + denominator = 30; + } else { + pv4l2_streamparm->parm.capture.timeperframe. + denominator = 25; + } + } + + pv4l2_streamparm->parm.capture.readbuffers = + peasycap->frame_buffer_many; + pv4l2_streamparm->parm.capture.extendedmode = 0; + if (copy_to_user((void __user *)arg, + pv4l2_streamparm, + sizeof(struct v4l2_streamparm))) { + kfree(pv4l2_streamparm); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EFAULT; + } + kfree(pv4l2_streamparm); + break; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_S_PARM: { + JOM(8, "VIDIOC_S_PARM unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_AUDIO: { + JOM(8, "VIDIOC_G_AUDIO unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_S_AUDIO: { + JOM(8, "VIDIOC_S_AUDIO unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_S_TUNER: { + JOM(8, "VIDIOC_S_TUNER unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_FBUF: + case VIDIOC_S_FBUF: + case VIDIOC_OVERLAY: { + JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + case VIDIOC_G_TUNER: { + JOM(8, "VIDIOC_G_TUNER unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } + case VIDIOC_G_FREQUENCY: + case VIDIOC_S_FREQUENCY: { + JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -EINVAL; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + default: { + JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ENOIOCTLCMD; + } + } + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + JOM(4, "unlocked easycapdc60_dongle[%i].mutex_video\n", kd); + return 0; +} +/*****************************************************************************/ diff --git a/drivers/staging/media/easycap/easycap_low.c b/drivers/staging/media/easycap/easycap_low.c new file mode 100644 index 000000000000..0385735ac6df --- /dev/null +++ b/drivers/staging/media/easycap/easycap_low.c @@ -0,0 +1,1129 @@ +/***************************************************************************** +* * +* * +* easycap_low.c * +* * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +/*****************************************************************************/ +/* + * ACKNOWLEGEMENTS AND REFERENCES + * ------------------------------ + * This driver makes use of register information contained in the Syntek + * Semicon DC-1125 driver hosted at + * http://sourceforge.net/projects/syntekdriver/. + * Particularly useful has been a patch to the latter driver provided by + * Ivor Hewitt in January 2009. The NTSC implementation is taken from the + * work of Ben Trask. +*/ +/****************************************************************************/ + +#include "easycap.h" + +#define GET(X, Y, Z) do { \ + int __rc; \ + *(Z) = (u16)0; \ + __rc = regget(X, Y, Z, sizeof(u8)); \ + if (0 > __rc) { \ + JOT(8, ":-(%i\n", __LINE__); return __rc; \ + } \ +} while (0) + +#define SET(X, Y, Z) do { \ + int __rc; \ + __rc = regset(X, Y, Z); \ + if (0 > __rc) { \ + JOT(8, ":-(%i\n", __LINE__); return __rc; \ + } \ +} while (0) + +/*--------------------------------------------------------------------------*/ +static const struct stk1160config { + int reg; + int set; +} stk1160configPAL[256] = { + {0x000, 0x0098}, + {0x002, 0x0093}, + + {0x001, 0x0003}, + {0x003, 0x0080}, + {0x00D, 0x0000}, + {0x00F, 0x0002}, + {0x018, 0x0010}, + {0x019, 0x0000}, + {0x01A, 0x0014}, + {0x01B, 0x000E}, + {0x01C, 0x0046}, + + {0x100, 0x0033}, + {0x103, 0x0000}, + {0x104, 0x0000}, + {0x105, 0x0000}, + {0x106, 0x0000}, + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* + * RESOLUTION 640x480 +*/ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + {0x110, 0x0008}, + {0x111, 0x0000}, + {0x112, 0x0020}, + {0x113, 0x0000}, + {0x114, 0x0508}, + {0x115, 0x0005}, + {0x116, 0x0110}, + {0x117, 0x0001}, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + {0x202, 0x000F}, + {0x203, 0x004A}, + {0x2FF, 0x0000}, + + {0xFFF, 0xFFFF} +}; +/*--------------------------------------------------------------------------*/ +static const struct stk1160config stk1160configNTSC[256] = { + {0x000, 0x0098}, + {0x002, 0x0093}, + + {0x001, 0x0003}, + {0x003, 0x0080}, + {0x00D, 0x0000}, + {0x00F, 0x0002}, + {0x018, 0x0010}, + {0x019, 0x0000}, + {0x01A, 0x0014}, + {0x01B, 0x000E}, + {0x01C, 0x0046}, + + {0x100, 0x0033}, + {0x103, 0x0000}, + {0x104, 0x0000}, + {0x105, 0x0000}, + {0x106, 0x0000}, + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +/* + * RESOLUTION 640x480 +*/ +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + {0x110, 0x0008}, + {0x111, 0x0000}, + {0x112, 0x0003}, + {0x113, 0x0000}, + {0x114, 0x0508}, + {0x115, 0x0005}, + {0x116, 0x00F3}, + {0x117, 0x0000}, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + {0x202, 0x000F}, + {0x203, 0x004A}, + {0x2FF, 0x0000}, + + {0xFFF, 0xFFFF} +}; +/*--------------------------------------------------------------------------*/ +static const struct saa7113config { + int reg; + int set; +} saa7113configPAL[256] = { + {0x01, 0x08}, + {0x02, 0x80}, + {0x03, 0x33}, + {0x04, 0x00}, + {0x05, 0x00}, + {0x06, 0xE9}, + {0x07, 0x0D}, + {0x08, 0x38}, + {0x09, 0x00}, + {0x0A, SAA_0A_DEFAULT}, + {0x0B, SAA_0B_DEFAULT}, + {0x0C, SAA_0C_DEFAULT}, + {0x0D, SAA_0D_DEFAULT}, + {0x0E, 0x01}, + {0x0F, 0x36}, + {0x10, 0x00}, + {0x11, 0x0C}, + {0x12, 0xE7}, + {0x13, 0x00}, + {0x15, 0x00}, + {0x16, 0x00}, + {0x40, 0x02}, + {0x41, 0xFF}, + {0x42, 0xFF}, + {0x43, 0xFF}, + {0x44, 0xFF}, + {0x45, 0xFF}, + {0x46, 0xFF}, + {0x47, 0xFF}, + {0x48, 0xFF}, + {0x49, 0xFF}, + {0x4A, 0xFF}, + {0x4B, 0xFF}, + {0x4C, 0xFF}, + {0x4D, 0xFF}, + {0x4E, 0xFF}, + {0x4F, 0xFF}, + {0x50, 0xFF}, + {0x51, 0xFF}, + {0x52, 0xFF}, + {0x53, 0xFF}, + {0x54, 0xFF}, + {0x55, 0xFF}, + {0x56, 0xFF}, + {0x57, 0xFF}, + {0x58, 0x40}, + {0x59, 0x54}, + {0x5A, 0x07}, + {0x5B, 0x83}, + + {0xFF, 0xFF} +}; +/*--------------------------------------------------------------------------*/ +static const struct saa7113config saa7113configNTSC[256] = { + {0x01, 0x08}, + {0x02, 0x80}, + {0x03, 0x33}, + {0x04, 0x00}, + {0x05, 0x00}, + {0x06, 0xE9}, + {0x07, 0x0D}, + {0x08, 0x78}, + {0x09, 0x00}, + {0x0A, SAA_0A_DEFAULT}, + {0x0B, SAA_0B_DEFAULT}, + {0x0C, SAA_0C_DEFAULT}, + {0x0D, SAA_0D_DEFAULT}, + {0x0E, 0x01}, + {0x0F, 0x36}, + {0x10, 0x00}, + {0x11, 0x0C}, + {0x12, 0xE7}, + {0x13, 0x00}, + {0x15, 0x00}, + {0x16, 0x00}, + {0x40, 0x82}, + {0x41, 0xFF}, + {0x42, 0xFF}, + {0x43, 0xFF}, + {0x44, 0xFF}, + {0x45, 0xFF}, + {0x46, 0xFF}, + {0x47, 0xFF}, + {0x48, 0xFF}, + {0x49, 0xFF}, + {0x4A, 0xFF}, + {0x4B, 0xFF}, + {0x4C, 0xFF}, + {0x4D, 0xFF}, + {0x4E, 0xFF}, + {0x4F, 0xFF}, + {0x50, 0xFF}, + {0x51, 0xFF}, + {0x52, 0xFF}, + {0x53, 0xFF}, + {0x54, 0xFF}, + {0x55, 0xFF}, + {0x56, 0xFF}, + {0x57, 0xFF}, + {0x58, 0x40}, + {0x59, 0x54}, + {0x5A, 0x0A}, + {0x5B, 0x83}, + + {0xFF, 0xFF} +}; + +static int regget(struct usb_device *pusb_device, + u16 index, void *reg, int reg_size) +{ + int rc; + + if (!pusb_device) + return -ENODEV; + + rc = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0), + 0x00, + (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE), + 0x00, + index, reg, reg_size, 50000); + + return rc; +} + +static int regset(struct usb_device *pusb_device, u16 index, u16 value) +{ + int rc; + + if (!pusb_device) + return -ENODEV; + + rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), + 0x01, + (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE), + value, index, NULL, 0, 500); + + if (rc < 0) + return rc; + + if (easycap_readback) { + u16 igot = 0; + rc = regget(pusb_device, index, &igot, sizeof(igot)); + igot = 0xFF & igot; + switch (index) { + case 0x000: + case 0x500: + case 0x502: + case 0x503: + case 0x504: + case 0x506: + case 0x507: + break; + + case 0x204: + case 0x205: + case 0x350: + case 0x351: + if (igot) + JOT(8, "unexpected 0x%02X " + "for STK register 0x%03X\n", + igot, index); + break; + + default: + if ((0xFF & value) != igot) + JOT(8, "unexpected 0x%02X != 0x%02X " + "for STK register 0x%03X\n", + igot, value, index); + break; + } + } + + return rc; +} +/*--------------------------------------------------------------------------*/ +/* + * FUNCTION wait_i2c() RETURNS 0 ON SUCCESS +*/ +/*--------------------------------------------------------------------------*/ +static int wait_i2c(struct usb_device *p) +{ + u16 get0; + u8 igot; + const int max = 2; + int k; + + if (!p) + return -ENODEV; + + for (k = 0; k < max; k++) { + GET(p, 0x0201, &igot); get0 = igot; + switch (get0) { + case 0x04: + case 0x01: + return 0; + case 0x00: + msleep(20); + continue; + default: + return get0 - 1; + } + } + return -1; +} + +/****************************************************************************/ +int confirm_resolution(struct usb_device *p) +{ + u8 get0, get1, get2, get3, get4, get5, get6, get7; + + if (!p) + return -ENODEV; + GET(p, 0x0110, &get0); + GET(p, 0x0111, &get1); + GET(p, 0x0112, &get2); + GET(p, 0x0113, &get3); + GET(p, 0x0114, &get4); + GET(p, 0x0115, &get5); + GET(p, 0x0116, &get6); + GET(p, 0x0117, &get7); + JOT(8, "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X\n", + get0, get1, get2, get3, get4, get5, get6, get7); + JOT(8, "....cf PAL_720x526: " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X\n", + 0x000, 0x000, 0x001, 0x000, 0x5A0, 0x005, 0x121, 0x001); + JOT(8, "....cf PAL_704x526: " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X\n", + 0x004, 0x000, 0x001, 0x000, 0x584, 0x005, 0x121, 0x001); + JOT(8, "....cf VGA_640x480: " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X, " + "0x%03X, 0x%03X\n", + 0x008, 0x000, 0x020, 0x000, 0x508, 0x005, 0x110, 0x001); + return 0; +} +/****************************************************************************/ +int confirm_stream(struct usb_device *p) +{ + u16 get2; + u8 igot; + + if (!p) + return -ENODEV; + GET(p, 0x0100, &igot); get2 = 0x80 & igot; + if (0x80 == get2) + JOT(8, "confirm_stream: OK\n"); + else + JOT(8, "confirm_stream: STUCK\n"); + return 0; +} +/****************************************************************************/ +int setup_stk(struct usb_device *p, bool ntsc) +{ + int i; + const struct stk1160config *cfg; + if (!p) + return -ENODEV; + cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL; + for (i = 0; cfg[i].reg != 0xFFF; i++) + SET(p, cfg[i].reg, cfg[i].set); + + write_300(p); + + return 0; +} +/****************************************************************************/ +int setup_saa(struct usb_device *p, bool ntsc) +{ + int i, ir; + const struct saa7113config *cfg; + if (!p) + return -ENODEV; + cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL; + for (i = 0; cfg[i].reg != 0xFF; i++) + ir = write_saa(p, cfg[i].reg, cfg[i].set); + return 0; +} +/****************************************************************************/ +int write_000(struct usb_device *p, u16 set2, u16 set0) +{ + u8 igot0, igot2; + + if (!p) + return -ENODEV; + GET(p, 0x0002, &igot2); + GET(p, 0x0000, &igot0); + SET(p, 0x0002, set2); + SET(p, 0x0000, set0); + return 0; +} +/****************************************************************************/ +int write_saa(struct usb_device *p, u16 reg0, u16 set0) +{ + if (!p) + return -ENODEV; + SET(p, 0x200, 0x00); + SET(p, 0x204, reg0); + SET(p, 0x205, set0); + SET(p, 0x200, 0x01); + return wait_i2c(p); +} +/****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?) + * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A + * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO SET + * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO SET + * REGISTER 504: TARGET ADDRESS ON VT1612A + */ +/*--------------------------------------------------------------------------*/ +int +write_vt(struct usb_device *p, u16 reg0, u16 set0) +{ + u8 igot; + u16 got502, got503; + u16 set502, set503; + + if (!p) + return -ENODEV; + SET(p, 0x0504, reg0); + SET(p, 0x0500, 0x008B); + + GET(p, 0x0502, &igot); got502 = (0xFF & igot); + GET(p, 0x0503, &igot); got503 = (0xFF & igot); + + JOT(16, "write_vt(., 0x%04X, 0x%04X): was 0x%04X\n", + reg0, set0, ((got503 << 8) | got502)); + + set502 = (0x00FF & set0); + set503 = ((0xFF00 & set0) >> 8); + + SET(p, 0x0504, reg0); + SET(p, 0x0502, set502); + SET(p, 0x0503, set503); + SET(p, 0x0500, 0x008C); + + return 0; +} +/****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?) + * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A + * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO GET + * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO GET + * REGISTER 504: TARGET ADDRESS ON VT1612A + */ +/*--------------------------------------------------------------------------*/ +int read_vt(struct usb_device *p, u16 reg0) +{ + u8 igot; + u16 got502, got503; + + if (!p) + return -ENODEV; + SET(p, 0x0504, reg0); + SET(p, 0x0500, 0x008B); + + GET(p, 0x0502, &igot); got502 = (0xFF & igot); + GET(p, 0x0503, &igot); got503 = (0xFF & igot); + + JOT(16, "read_vt(., 0x%04X): has 0x%04X\n", + reg0, ((got503 << 8) | got502)); + + return (got503 << 8) | got502; +} +/****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * THESE APPEAR TO HAVE NO EFFECT ON EITHER VIDEO OR AUDIO. + */ +/*--------------------------------------------------------------------------*/ +int write_300(struct usb_device *p) +{ + if (!p) + return -ENODEV; + SET(p, 0x300, 0x0012); + SET(p, 0x350, 0x002D); + SET(p, 0x351, 0x0001); + SET(p, 0x352, 0x0000); + SET(p, 0x353, 0x0000); + SET(p, 0x300, 0x0080); + return 0; +} +/****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * NOTE: THE FOLLOWING IS NOT CHECKED: + * REGISTER 0x0F, WHICH IS INVOLVED IN CHROMINANCE AUTOMATIC GAIN CONTROL. + */ +/*--------------------------------------------------------------------------*/ +int check_saa(struct usb_device *p, bool ntsc) +{ + int i, ir, rc = 0; + struct saa7113config const *cfg; + if (!p) + return -ENODEV; + + cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL; + for (i = 0; cfg[i].reg != 0xFF; i++) { + if (0x0F == cfg[i].reg) + continue; + ir = read_saa(p, cfg[i].reg); + if (ir != cfg[i].set) { + SAY("SAA register 0x%02X has 0x%02X, expected 0x%02X\n", + cfg[i].reg, ir, cfg[i].set); + rc--; + } + } + + return (rc < -8) ? rc : 0; +} +/****************************************************************************/ +int merit_saa(struct usb_device *p) +{ + int rc; + + if (!p) + return -ENODEV; + rc = read_saa(p, 0x1F); + return ((0 > rc) || (0x02 & rc)) ? 1 : 0; +} +/****************************************************************************/ +int ready_saa(struct usb_device *p) +{ + int j, rc, rate; + const int max = 5, marktime = PATIENCE/5; +/*--------------------------------------------------------------------------*/ +/* + * RETURNS 0 FOR INTERLACED 50 Hz + * 1 FOR NON-INTERLACED 50 Hz + * 2 FOR INTERLACED 60 Hz + * 3 FOR NON-INTERLACED 60 Hz +*/ +/*--------------------------------------------------------------------------*/ + if (!p) + return -ENODEV; + j = 0; + while (max > j) { + rc = read_saa(p, 0x1F); + if (0 <= rc) { + if (0 == (0x40 & rc)) + break; + if (1 == (0x01 & rc)) + break; + } + msleep(marktime); + j++; + } + if (max == j) + return -1; + else { + if (0x20 & rc) { + rate = 2; + JOT(8, "hardware detects 60 Hz\n"); + } else { + rate = 0; + JOT(8, "hardware detects 50 Hz\n"); + } + if (0x80 & rc) + JOT(8, "hardware detects interlacing\n"); + else { + rate++; + JOT(8, "hardware detects no interlacing\n"); + } + } + return 0; +} +/****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * NOTE: THE FOLLOWING ARE NOT CHECKED: + * REGISTERS 0x000, 0x002: FUNCTIONALITY IS NOT KNOWN + * REGISTER 0x100: ACCEPT ALSO (0x80 | stk1160config....[.].set) + */ +/*--------------------------------------------------------------------------*/ +int check_stk(struct usb_device *p, bool ntsc) +{ + int i, ir; + const struct stk1160config *cfg; + + if (!p) + return -ENODEV; + cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL; + + for (i = 0; 0xFFF != cfg[i].reg; i++) { + if (0x000 == cfg[i].reg || 0x002 == cfg[i].reg) + continue; + + + ir = read_stk(p, cfg[i].reg); + if (0x100 == cfg[i].reg) { + if ((ir != (0xFF & cfg[i].set)) && + (ir != (0x80 | (0xFF & cfg[i].set))) && + (0xFFFF != cfg[i].set)) { + SAY("STK reg[0x%03X]=0x%02X expected 0x%02X\n", + cfg[i].reg, ir, cfg[i].set); + } + continue; + } + if ((ir != (0xFF & cfg[i].set)) && (0xFFFF != cfg[i].set)) + SAY("STK register 0x%03X has 0x%02X,expected 0x%02X\n", + cfg[i].reg, ir, cfg[i].set); + } + return 0; +} +/****************************************************************************/ +int read_saa(struct usb_device *p, u16 reg0) +{ + u8 igot; + + if (!p) + return -ENODEV; + SET(p, 0x208, reg0); + SET(p, 0x200, 0x20); + if (0 != wait_i2c(p)) + return -1; + igot = 0; + GET(p, 0x0209, &igot); + return igot; +} +/****************************************************************************/ +int read_stk(struct usb_device *p, u32 reg0) +{ + u8 igot; + + if (!p) + return -ENODEV; + igot = 0; + GET(p, reg0, &igot); + return igot; +} +/****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * HARDWARE USERSPACE INPUT NUMBER PHYSICAL INPUT DRIVER input VALUE + * + * CVBS+S-VIDEO 0 or 1 CVBS 1 + * FOUR-CVBS 0 or 1 CVBS1 1 + * FOUR-CVBS 2 CVBS2 2 + * FOUR-CVBS 3 CVBS3 3 + * FOUR-CVBS 4 CVBS4 4 + * CVBS+S-VIDEO 5 S-VIDEO 5 + * + * WHEN 5==input THE ARGUMENT mode MUST ALSO BE SUPPLIED: + * + * mode 7 => GAIN TO BE SET EXPLICITLY USING REGISTER 0x05 (UNTESTED) + * mode 9 => USE AUTOMATIC GAIN CONTROL (DEFAULT) + * +*/ +/*---------------------------------------------------------------------------*/ +int +select_input(struct usb_device *p, int input, int mode) +{ + int ir; + + if (!p) + return -ENODEV; + stop_100(p); + switch (input) { + case 0: + case 1: { + if (0 != write_saa(p, 0x02, 0x80)) + SAY("ERROR: failed to set SAA register 0x02 " + "for input %i\n", input); + + SET(p, 0x0000, 0x0098); + SET(p, 0x0002, 0x0078); + break; + } + case 2: { + if (0 != write_saa(p, 0x02, 0x80)) + SAY("ERROR: failed to set SAA register 0x02 " + "for input %i\n", input); + + SET(p, 0x0000, 0x0090); + SET(p, 0x0002, 0x0078); + break; + } + case 3: { + if (0 != write_saa(p, 0x02, 0x80)) + SAY("ERROR: failed to set SAA register 0x02 " + " for input %i\n", input); + + SET(p, 0x0000, 0x0088); + SET(p, 0x0002, 0x0078); + break; + } + case 4: { + if (0 != write_saa(p, 0x02, 0x80)) { + SAY("ERROR: failed to set SAA register 0x02 " + "for input %i\n", input); + } + SET(p, 0x0000, 0x0080); + SET(p, 0x0002, 0x0078); + break; + } + case 5: { + if (9 != mode) + mode = 7; + switch (mode) { + case 7: { + if (0 != write_saa(p, 0x02, 0x87)) + SAY("ERROR: failed to set SAA register 0x02 " + "for input %i\n", input); + + if (0 != write_saa(p, 0x05, 0xFF)) + SAY("ERROR: failed to set SAA register 0x05 " + "for input %i\n", input); + + break; + } + case 9: { + if (0 != write_saa(p, 0x02, 0x89)) + SAY("ERROR: failed to set SAA register 0x02 " + "for input %i\n", input); + + if (0 != write_saa(p, 0x05, 0x00)) + SAY("ERROR: failed to set SAA register 0x05 " + "for input %i\n", input); + + break; + } + default: + SAY("MISTAKE: bad mode: %i\n", mode); + return -1; + } + + if (0 != write_saa(p, 0x04, 0x00)) + SAY("ERROR: failed to set SAA register 0x04 " + "for input %i\n", input); + + if (0 != write_saa(p, 0x09, 0x80)) + SAY("ERROR: failed to set SAA register 0x09 " + "for input %i\n", input); + + SET(p, 0x0002, 0x0093); + break; + } + default: + SAY("ERROR: bad input: %i\n", input); + return -1; + } + + ir = read_stk(p, 0x00); + JOT(8, "STK register 0x00 has 0x%02X\n", ir); + ir = read_saa(p, 0x02); + JOT(8, "SAA register 0x02 has 0x%02X\n", ir); + + start_100(p); + + return 0; +} +/****************************************************************************/ +int set_resolution(struct usb_device *p, + u16 set0, u16 set1, u16 set2, u16 set3) +{ + u16 u0x0111, u0x0113, u0x0115, u0x0117; + + if (!p) + return -ENODEV; + u0x0111 = ((0xFF00 & set0) >> 8); + u0x0113 = ((0xFF00 & set1) >> 8); + u0x0115 = ((0xFF00 & set2) >> 8); + u0x0117 = ((0xFF00 & set3) >> 8); + + SET(p, 0x0110, (0x00FF & set0)); + SET(p, 0x0111, u0x0111); + SET(p, 0x0112, (0x00FF & set1)); + SET(p, 0x0113, u0x0113); + SET(p, 0x0114, (0x00FF & set2)); + SET(p, 0x0115, u0x0115); + SET(p, 0x0116, (0x00FF & set3)); + SET(p, 0x0117, u0x0117); + + return 0; +} +/****************************************************************************/ +int start_100(struct usb_device *p) +{ + u16 get116, get117, get0; + u8 igot116, igot117, igot; + + if (!p) + return -ENODEV; + GET(p, 0x0116, &igot116); + get116 = igot116; + GET(p, 0x0117, &igot117); + get117 = igot117; + SET(p, 0x0116, 0x0000); + SET(p, 0x0117, 0x0000); + + GET(p, 0x0100, &igot); + get0 = igot; + SET(p, 0x0100, (0x80 | get0)); + + SET(p, 0x0116, get116); + SET(p, 0x0117, get117); + + return 0; +} +/****************************************************************************/ +int stop_100(struct usb_device *p) +{ + u16 get0; + u8 igot; + + if (!p) + return -ENODEV; + GET(p, 0x0100, &igot); + get0 = igot; + SET(p, 0x0100, (0x7F & get0)); + return 0; +} +/****************************************************************************/ +/****************************************************************************/ +/*****************************************************************************/ +int wakeup_device(struct usb_device *pusb_device) +{ + if (!pusb_device) + return -ENODEV; + return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), + USB_REQ_SET_FEATURE, + USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE, + USB_DEVICE_REMOTE_WAKEUP, + 0, NULL, 0, 50000); +} +/*****************************************************************************/ +int +audio_setup(struct easycap *peasycap) +{ + struct usb_device *pusb_device; + u8 buffer[1]; + int rc, id1, id2; +/*---------------------------------------------------------------------------*/ +/* + * IMPORTANT: + * THE MESSAGE OF TYPE (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) + * CAUSES MUTING IF THE VALUE 0x0100 IS SENT. + * TO ENABLE AUDIO THE VALUE 0x0200 MUST BE SENT. + */ +/*---------------------------------------------------------------------------*/ + const u8 request = 0x01; + const u8 requesttype = USB_DIR_OUT | + USB_TYPE_CLASS | + USB_RECIP_INTERFACE; + const u16 value_unmute = 0x0200; + const u16 index = 0x0301; + const u16 length = 1; + + if (!peasycap) + return -EFAULT; + + pusb_device = peasycap->pusb_device; + if (!pusb_device) + return -ENODEV; + + JOM(8, "%02X %02X %02X %02X %02X %02X %02X %02X\n", + requesttype, request, + (0x00FF & value_unmute), + (0xFF00 & value_unmute) >> 8, + (0x00FF & index), + (0xFF00 & index) >> 8, + (0x00FF & length), + (0xFF00 & length) >> 8); + + buffer[0] = 0x01; + + rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), + request, requesttype, value_unmute, + index, &buffer[0], length, 50000); + + JOT(8, "0x%02X=buffer\n", buffer[0]); + if (rc != (int)length) { + switch (rc) { + case -EPIPE: + SAY("usb_control_msg returned -EPIPE\n"); + break; + default: + SAY("ERROR: usb_control_msg returned %i\n", rc); + break; + } + } +/*--------------------------------------------------------------------------*/ +/* + * REGISTER 500: SETTING VALUE TO 0x0094 RESETS AUDIO CONFIGURATION ??? + * REGISTER 506: ANALOGUE AUDIO ATTENTUATOR ??? + * FOR THE CVBS+S-VIDEO HARDWARE: + * SETTING VALUE TO 0x0000 GIVES QUIET SOUND. + * THE UPPER BYTE SEEMS TO HAVE NO EFFECT. + * FOR THE FOUR-CVBS HARDWARE: + * SETTING VALUE TO 0x0000 SEEMS TO HAVE NO EFFECT. + * REGISTER 507: ANALOGUE AUDIO PREAMPLIFIER ON/OFF ??? + * FOR THE CVBS-S-VIDEO HARDWARE: + * SETTING VALUE TO 0x0001 GIVES VERY LOUD, DISTORTED SOUND. + * THE UPPER BYTE SEEMS TO HAVE NO EFFECT. + */ +/*--------------------------------------------------------------------------*/ + SET(pusb_device, 0x0500, 0x0094); + SET(pusb_device, 0x0500, 0x008C); + SET(pusb_device, 0x0506, 0x0001); + SET(pusb_device, 0x0507, 0x0000); + id1 = read_vt(pusb_device, 0x007C); + id2 = read_vt(pusb_device, 0x007E); + SAM("0x%04X:0x%04X is audio vendor id\n", id1, id2); +/*---------------------------------------------------------------------------*/ +/* + * SELECT AUDIO SOURCE "LINE IN" AND SET THE AUDIO GAIN. +*/ +/*---------------------------------------------------------------------------*/ + if (0 != audio_gainset(pusb_device, peasycap->gain)) + SAY("ERROR: audio_gainset() failed\n"); + check_vt(pusb_device); + return 0; +} +/*****************************************************************************/ +int check_vt(struct usb_device *pusb_device) +{ + int igot; + + if (!pusb_device) + return -ENODEV; + igot = read_vt(pusb_device, 0x0002); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x02\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x02); + + igot = read_vt(pusb_device, 0x000E); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x0E\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x0E); + + igot = read_vt(pusb_device, 0x0010); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x10\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x10); + + igot = read_vt(pusb_device, 0x0012); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x12\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x12); + + igot = read_vt(pusb_device, 0x0014); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x14\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x14); + + igot = read_vt(pusb_device, 0x0016); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x16\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x16); + + igot = read_vt(pusb_device, 0x0018); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x18\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x18); + + igot = read_vt(pusb_device, 0x001C); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x1C\n"); + if (0x8000 & igot) + SAY("register 0x%02X muted\n", 0x1C); + + return 0; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* NOTE: THIS DOES INCREASE THE VOLUME DRAMATICALLY: + * audio_gainset(pusb_device, 0x000F); + * + * loud dB register 0x10 dB register 0x1C dB total + * 0 -34.5 0 -34.5 + * .. .... . .... + * 15 10.5 0 10.5 + * 16 12.0 0 12.0 + * 17 12.0 1.5 13.5 + * .. .... .... .... + * 31 12.0 22.5 34.5 +*/ +/*---------------------------------------------------------------------------*/ +int audio_gainset(struct usb_device *pusb_device, s8 loud) +{ + int igot; + u8 tmp; + u16 mute; + + if (!pusb_device) + return -ENODEV; + if (0 > loud) + loud = 0; + if (31 < loud) + loud = 31; + + write_vt(pusb_device, 0x0002, 0x8000); +/*---------------------------------------------------------------------------*/ + igot = read_vt(pusb_device, 0x000E); + if (0 > igot) { + SAY("ERROR: failed to read VT1612A register 0x0E\n"); + mute = 0x0000; + } else + mute = 0x8000 & ((unsigned int)igot); + mute = 0; + + if (16 > loud) + tmp = 0x01 | (0x001F & (((u8)(15 - loud)) << 1)); + else + tmp = 0; + + JOT(8, "0x%04X=(mute|tmp) for VT1612A register 0x0E\n", mute | tmp); + write_vt(pusb_device, 0x000E, (mute | tmp)); +/*---------------------------------------------------------------------------*/ + igot = read_vt(pusb_device, 0x0010); + if (0 > igot) { + SAY("ERROR: failed to read VT1612A register 0x10\n"); + mute = 0x0000; + } else + mute = 0x8000 & ((unsigned int)igot); + mute = 0; + + JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x10,...0x18\n", + mute | tmp | (tmp << 8)); + write_vt(pusb_device, 0x0010, (mute | tmp | (tmp << 8))); + write_vt(pusb_device, 0x0012, (mute | tmp | (tmp << 8))); + write_vt(pusb_device, 0x0014, (mute | tmp | (tmp << 8))); + write_vt(pusb_device, 0x0016, (mute | tmp | (tmp << 8))); + write_vt(pusb_device, 0x0018, (mute | tmp | (tmp << 8))); +/*---------------------------------------------------------------------------*/ + igot = read_vt(pusb_device, 0x001C); + if (0 > igot) { + SAY("ERROR: failed to read VT1612A register 0x1C\n"); + mute = 0x0000; + } else + mute = 0x8000 & ((unsigned int)igot); + mute = 0; + + if (16 <= loud) + tmp = 0x000F & (u8)(loud - 16); + else + tmp = 0; + + JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x1C\n", + mute | tmp | (tmp << 8)); + write_vt(pusb_device, 0x001C, (mute | tmp | (tmp << 8))); + write_vt(pusb_device, 0x001A, 0x0404); + write_vt(pusb_device, 0x0002, 0x0000); + return 0; +} +/*****************************************************************************/ +int audio_gainget(struct usb_device *pusb_device) +{ + int igot; + + if (!pusb_device) + return -ENODEV; + igot = read_vt(pusb_device, 0x001C); + if (0 > igot) + SAY("ERROR: failed to read VT1612A register 0x1C\n"); + return igot; +} +/*****************************************************************************/ diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c new file mode 100644 index 000000000000..a45c0b507067 --- /dev/null +++ b/drivers/staging/media/easycap/easycap_main.c @@ -0,0 +1,4253 @@ +/****************************************************************************** +* * +* easycap_main.c * +* * +* Video driver for EasyCAP USB2.0 Video Capture Device DC60 * +* * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +/*****************************************************************************/ + +#include "easycap.h" +#include + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("R.M. Thomas "); +MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION); +MODULE_VERSION(EASYCAP_DRIVER_VERSION); + +#ifdef CONFIG_EASYCAP_DEBUG +int easycap_debug; +module_param_named(debug, easycap_debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level: 0(default),1,2,...,9"); +#endif /* CONFIG_EASYCAP_DEBUG */ + +bool easycap_readback; +module_param_named(readback, easycap_readback, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(readback, "read back written registers: (default false)"); + +static int easycap_bars = 1; +module_param_named(bars, easycap_bars, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(bars, + "Testcard bars on input signal failure: 0=>no, 1=>yes(default)"); + +static int easycap_gain = 16; +module_param_named(gain, easycap_gain, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(gain, "Audio gain: 0,...,16(default),...31"); + +static bool easycap_ntsc; +module_param_named(ntsc, easycap_ntsc, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(ntsc, "NTSC default encoding (default PAL)"); + + + +struct easycap_dongle easycapdc60_dongle[DONGLE_MANY]; +static struct mutex mutex_dongle; +static void easycap_complete(struct urb *purb); +static int reset(struct easycap *peasycap); + +const char *strerror(int err) +{ +#define ERRNOSTR(_e) case _e: return # _e + switch (err) { + case 0: return "OK"; + ERRNOSTR(ENOMEM); + ERRNOSTR(ENODEV); + ERRNOSTR(ENXIO); + ERRNOSTR(EINVAL); + ERRNOSTR(EAGAIN); + ERRNOSTR(EFBIG); + ERRNOSTR(EPIPE); + ERRNOSTR(EMSGSIZE); + ERRNOSTR(ENOSPC); + ERRNOSTR(EINPROGRESS); + ERRNOSTR(ENOSR); + ERRNOSTR(EOVERFLOW); + ERRNOSTR(EPROTO); + ERRNOSTR(EILSEQ); + ERRNOSTR(ETIMEDOUT); + ERRNOSTR(EOPNOTSUPP); + ERRNOSTR(EPFNOSUPPORT); + ERRNOSTR(EAFNOSUPPORT); + ERRNOSTR(EADDRINUSE); + ERRNOSTR(EADDRNOTAVAIL); + ERRNOSTR(ENOBUFS); + ERRNOSTR(EISCONN); + ERRNOSTR(ENOTCONN); + ERRNOSTR(ESHUTDOWN); + ERRNOSTR(ENOENT); + ERRNOSTR(ECONNRESET); + ERRNOSTR(ETIME); + ERRNOSTR(ECOMM); + ERRNOSTR(EREMOTEIO); + ERRNOSTR(EXDEV); + ERRNOSTR(EPERM); + default: return "unknown"; + } + +#undef ERRNOSTR +} + +/*---------------------------------------------------------------------------*/ +/* + * PARAMETERS USED WHEN REGISTERING THE VIDEO INTERFACE + * + * NOTE: SOME KERNELS IGNORE usb_class_driver.minor_base, AS MENTIONED BY + * CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGE 253. + * THIS IS THE CASE FOR OpenSUSE. + */ +/*---------------------------------------------------------------------------*/ +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +/****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * THIS ROUTINE DOES NOT DETECT DUPLICATE OCCURRENCES OF POINTER peasycap +*/ +/*---------------------------------------------------------------------------*/ +int isdongle(struct easycap *peasycap) +{ + int k; + if (!peasycap) + return -2; + for (k = 0; k < DONGLE_MANY; k++) { + if (easycapdc60_dongle[k].peasycap == peasycap) { + peasycap->isdongle = k; + return k; + } + } + return -1; +} +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +static int easycap_open(struct inode *inode, struct file *file) +{ + struct video_device *pvideo_device; + struct easycap *peasycap; + int rc; + + JOT(4, "\n"); + SAY("==========OPEN=========\n"); + + pvideo_device = video_devdata(file); + if (!pvideo_device) { + SAY("ERROR: pvideo_device is NULL.\n"); + return -EFAULT; + } + peasycap = (struct easycap *)video_get_drvdata(pvideo_device); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } else { + JOM(16, "peasycap->pusb_device=%p\n", peasycap->pusb_device); + } + file->private_data = peasycap; + rc = wakeup_device(peasycap->pusb_device); + if (0 == rc) + JOM(8, "wakeup_device() OK\n"); + else { + SAM("ERROR: wakeup_device() rc = %i\n", rc); + if (-ENODEV == rc) + SAM("ERROR: wakeup_device() returned -ENODEV\n"); + else + SAM("ERROR: wakeup_device() rc = %i\n", rc); + return rc; + } + peasycap->input = 0; + rc = reset(peasycap); + if (rc) { + SAM("ERROR: reset() rc = %i\n", rc); + return -EFAULT; + } + return 0; +} + +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * RESET THE HARDWARE TO ITS REFERENCE STATE. + * + * THIS ROUTINE MAY BE CALLED REPEATEDLY IF easycap_complete() DETECTS + * A BAD VIDEO FRAME SIZE. +*/ +/*---------------------------------------------------------------------------*/ +static int reset(struct easycap *peasycap) +{ + struct easycap_standard const *peasycap_standard; + int fmtidx, input, rate; + bool ntsc, other; + int rc; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + input = peasycap->input; + +/*---------------------------------------------------------------------------*/ +/* + * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED + * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR + * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE + * A SWITCH BETWEEN PAL AND NTSC. + * + * FUNCTION ready_saa() MAY REQUIRE A SUBSTANTIAL FRACTION OF A SECOND TO + * COMPLETE, SO SHOULD NOT BE INVOKED WITHOUT GOOD REASON. +*/ +/*---------------------------------------------------------------------------*/ + other = false; + JOM(8, "peasycap->ntsc=%d\n", peasycap->ntsc); + + rate = ready_saa(peasycap->pusb_device); + if (rate < 0) { + JOM(8, "not ready to capture after %i ms ...\n", PATIENCE); + ntsc = !peasycap->ntsc; + JOM(8, "... trying %s ..\n", ntsc ? "NTSC" : "PAL"); + rc = setup_stk(peasycap->pusb_device, ntsc); + if (rc) { + SAM("ERROR: setup_stk() rc = %i\n", rc); + return -EFAULT; + } + rc = setup_saa(peasycap->pusb_device, ntsc); + if (rc) { + SAM("ERROR: setup_saa() rc = %i\n", rc); + return -EFAULT; + } + + rate = ready_saa(peasycap->pusb_device); + if (rate < 0) { + JOM(8, "not ready to capture after %i ms\n", PATIENCE); + JOM(8, "... saa register 0x1F has 0x%02X\n", + read_saa(peasycap->pusb_device, 0x1F)); + ntsc = peasycap->ntsc; + } else { + JOM(8, "... success at second try: %i=rate\n", rate); + ntsc = (0 < (rate/2)) ? true : false ; + other = true; + } + } else { + JOM(8, "... success at first try: %i=rate\n", rate); + ntsc = (0 < rate/2) ? true : false ; + } + JOM(8, "ntsc=%d\n", ntsc); +/*---------------------------------------------------------------------------*/ + + rc = setup_stk(peasycap->pusb_device, ntsc); + if (rc) { + SAM("ERROR: setup_stk() rc = %i\n", rc); + return -EFAULT; + } + rc = setup_saa(peasycap->pusb_device, ntsc); + if (rc) { + SAM("ERROR: setup_saa() rc = %i\n", rc); + return -EFAULT; + } + + memset(peasycap->merit, 0, sizeof(peasycap->merit)); + + peasycap->video_eof = 0; + peasycap->audio_eof = 0; +/*---------------------------------------------------------------------------*/ +/* + * RESTORE INPUT AND FORCE REFRESH OF STANDARD, FORMAT, ETC. + * + * WHILE THIS PROCEDURE IS IN PROGRESS, SOME IOCTL COMMANDS WILL RETURN -EBUSY. +*/ +/*---------------------------------------------------------------------------*/ + peasycap->input = -8192; + peasycap->standard_offset = -8192; + fmtidx = ntsc ? NTSC_M : PAL_BGHIN; + if (other) { + peasycap_standard = &easycap_standard[0]; + while (0xFFFF != peasycap_standard->mask) { + if (fmtidx == peasycap_standard->v4l2_standard.index) { + peasycap->inputset[input].standard_offset = + peasycap_standard - easycap_standard; + break; + } + peasycap_standard++; + } + if (0xFFFF == peasycap_standard->mask) { + SAM("ERROR: standard not found\n"); + return -EINVAL; + } + JOM(8, "%i=peasycap->inputset[%i].standard_offset\n", + peasycap->inputset[input].standard_offset, input); + } + peasycap->format_offset = -8192; + peasycap->brightness = -8192; + peasycap->contrast = -8192; + peasycap->saturation = -8192; + peasycap->hue = -8192; + + rc = newinput(peasycap, input); + + if (rc) { + SAM("ERROR: newinput(.,%i) rc = %i\n", rc, input); + return -EFAULT; + } + JOM(4, "restored input, standard and format\n"); + + JOM(8, "true=peasycap->ntsc %d\n", peasycap->ntsc); + + if (0 > peasycap->input) { + SAM("MISTAKE: %i=peasycap->input\n", peasycap->input); + return -ENOENT; + } + if (0 > peasycap->standard_offset) { + SAM("MISTAKE: %i=peasycap->standard_offset\n", + peasycap->standard_offset); + return -ENOENT; + } + if (0 > peasycap->format_offset) { + SAM("MISTAKE: %i=peasycap->format_offset\n", + peasycap->format_offset); + return -ENOENT; + } + if (0 > peasycap->brightness) { + SAM("MISTAKE: %i=peasycap->brightness\n", + peasycap->brightness); + return -ENOENT; + } + if (0 > peasycap->contrast) { + SAM("MISTAKE: %i=peasycap->contrast\n", peasycap->contrast); + return -ENOENT; + } + if (0 > peasycap->saturation) { + SAM("MISTAKE: %i=peasycap->saturation\n", + peasycap->saturation); + return -ENOENT; + } + if (0 > peasycap->hue) { + SAM("MISTAKE: %i=peasycap->hue\n", peasycap->hue); + return -ENOENT; + } + return 0; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * IF THE REQUESTED INPUT IS THE SAME AS THE EXISTING INPUT, DO NOTHING. + * OTHERWISE: + * KILL URBS, CLEAR FIELD AND FRAME BUFFERS AND RESET THEIR + * _read AND _fill POINTERS. + * SELECT THE NEW INPUT. + * ADJUST THE STANDARD, FORMAT, BRIGHTNESS, CONTRAST, SATURATION AND HUE + * ON THE BASIS OF INFORMATION IN STRUCTURE easycap.inputset[input]. + * RESUBMIT THE URBS IF STREAMING WAS ALREADY IN PROGRESS. + * + * NOTE: + * THIS ROUTINE MAY BE CALLED FREQUENTLY BY ZONEMINDER VIA IOCTL, + * SO IT SHOULD WRITE ONLY SPARINGLY TO THE LOGFILE. +*/ +/*---------------------------------------------------------------------------*/ +int +newinput(struct easycap *peasycap, int input) +{ + int rc, k, m, mood, off; + int inputnow, video_idlenow, audio_idlenow; + bool resubmit; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + JOM(8, "%i=input sought\n", input); + + if (0 > input && INPUT_MANY <= input) + return -ENOENT; + inputnow = peasycap->input; + if (input == inputnow) + return 0; +/*---------------------------------------------------------------------------*/ +/* + * IF STREAMING IS IN PROGRESS THE URBS ARE KILLED AT THIS + * STAGE AND WILL BE RESUBMITTED PRIOR TO EXIT FROM THE ROUTINE. + * IF NO STREAMING IS IN PROGRESS NO URBS WILL BE SUBMITTED BY THE + * ROUTINE. +*/ +/*---------------------------------------------------------------------------*/ + video_idlenow = peasycap->video_idle; + audio_idlenow = peasycap->audio_idle; + + peasycap->video_idle = 1; + peasycap->audio_idle = 1; + if (peasycap->video_isoc_streaming) { + resubmit = true; + kill_video_urbs(peasycap); + } else { + resubmit = false; + } +/*---------------------------------------------------------------------------*/ + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -ENODEV; + } + rc = usb_set_interface(peasycap->pusb_device, + peasycap->video_interface, + peasycap->video_altsetting_off); + if (rc) { + SAM("ERROR: usb_set_interface() rc = %i\n", rc); + return -EFAULT; + } + rc = stop_100(peasycap->pusb_device); + if (rc) { + SAM("ERROR: stop_100() rc = %i\n", rc); + return -EFAULT; + } + for (k = 0; k < FIELD_BUFFER_MANY; k++) { + for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) + memset(peasycap->field_buffer[k][m].pgo, 0, PAGE_SIZE); + } + for (k = 0; k < FRAME_BUFFER_MANY; k++) { + for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) + memset(peasycap->frame_buffer[k][m].pgo, 0, PAGE_SIZE); + } + peasycap->field_page = 0; + peasycap->field_read = 0; + peasycap->field_fill = 0; + + peasycap->frame_read = 0; + peasycap->frame_fill = 0; + for (k = 0; k < peasycap->input; k++) { + (peasycap->frame_fill)++; + if (peasycap->frame_buffer_many <= peasycap->frame_fill) + peasycap->frame_fill = 0; + } + peasycap->input = input; + select_input(peasycap->pusb_device, peasycap->input, 9); +/*---------------------------------------------------------------------------*/ + if (input == peasycap->inputset[input].input) { + off = peasycap->inputset[input].standard_offset; + if (off != peasycap->standard_offset) { + rc = adjust_standard(peasycap, + easycap_standard[off].v4l2_standard.id); + if (rc) { + SAM("ERROR: adjust_standard() rc = %i\n", rc); + return -EFAULT; + } + JOM(8, "%i=peasycap->standard_offset\n", + peasycap->standard_offset); + } else { + JOM(8, "%i=peasycap->standard_offset unchanged\n", + peasycap->standard_offset); + } + off = peasycap->inputset[input].format_offset; + if (off != peasycap->format_offset) { + struct v4l2_pix_format *pix = + &easycap_format[off].v4l2_format.fmt.pix; + rc = adjust_format(peasycap, + pix->width, pix->height, + pix->pixelformat, pix->field, false); + if (0 > rc) { + SAM("ERROR: adjust_format() rc = %i\n", rc); + return -EFAULT; + } + JOM(8, "%i=peasycap->format_offset\n", + peasycap->format_offset); + } else { + JOM(8, "%i=peasycap->format_offset unchanged\n", + peasycap->format_offset); + } + mood = peasycap->inputset[input].brightness; + if (mood != peasycap->brightness) { + rc = adjust_brightness(peasycap, mood); + if (rc) { + SAM("ERROR: adjust_brightness rc = %i\n", rc); + return -EFAULT; + } + JOM(8, "%i=peasycap->brightness\n", + peasycap->brightness); + } + mood = peasycap->inputset[input].contrast; + if (mood != peasycap->contrast) { + rc = adjust_contrast(peasycap, mood); + if (rc) { + SAM("ERROR: adjust_contrast rc = %i\n", rc); + return -EFAULT; + } + JOM(8, "%i=peasycap->contrast\n", peasycap->contrast); + } + mood = peasycap->inputset[input].saturation; + if (mood != peasycap->saturation) { + rc = adjust_saturation(peasycap, mood); + if (rc) { + SAM("ERROR: adjust_saturation rc = %i\n", rc); + return -EFAULT; + } + JOM(8, "%i=peasycap->saturation\n", + peasycap->saturation); + } + mood = peasycap->inputset[input].hue; + if (mood != peasycap->hue) { + rc = adjust_hue(peasycap, mood); + if (rc) { + SAM("ERROR: adjust_hue rc = %i\n", rc); + return -EFAULT; + } + JOM(8, "%i=peasycap->hue\n", peasycap->hue); + } + } else { + SAM("MISTAKE: easycap.inputset[%i] unpopulated\n", input); + return -ENOENT; + } +/*---------------------------------------------------------------------------*/ + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -ENODEV; + } + rc = usb_set_interface(peasycap->pusb_device, + peasycap->video_interface, + peasycap->video_altsetting_on); + if (rc) { + SAM("ERROR: usb_set_interface() rc = %i\n", rc); + return -EFAULT; + } + rc = start_100(peasycap->pusb_device); + if (rc) { + SAM("ERROR: start_100() rc = %i\n", rc); + return -EFAULT; + } + if (resubmit) + submit_video_urbs(peasycap); + + peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1; + peasycap->video_idle = video_idlenow; + peasycap->audio_idle = audio_idlenow; + peasycap->video_junk = 0; + + return 0; +} +/*****************************************************************************/ +int submit_video_urbs(struct easycap *peasycap) +{ + struct data_urb *pdata_urb; + struct urb *purb; + struct list_head *plist_head; + int j, isbad, nospc, m, rc; + int isbuf; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + + if (!peasycap->purb_video_head) { + SAY("ERROR: peasycap->urb_video_head uninitialized\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAY("ERROR: peasycap->pusb_device is NULL\n"); + return -ENODEV; + } + if (!peasycap->video_isoc_streaming) { + JOM(4, "submission of all video urbs\n"); + isbad = 0; nospc = 0; m = 0; + list_for_each(plist_head, (peasycap->purb_video_head)) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + purb = pdata_urb->purb; + isbuf = pdata_urb->isbuf; + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = + usb_rcvisocpipe(peasycap->pusb_device, + peasycap->video_endpointnumber); + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = + peasycap->video_isoc_buffer[isbuf].pgo; + purb->transfer_buffer_length = + peasycap->video_isoc_buffer_size; + purb->complete = easycap_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = + peasycap->video_isoc_framesperdesc; + + for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j]. offset = + j * peasycap->video_isoc_maxframesize; + purb->iso_frame_desc[j]. length = + peasycap->video_isoc_maxframesize; + } + + rc = usb_submit_urb(purb, GFP_KERNEL); + if (rc) { + isbad++; + SAM("ERROR: usb_submit_urb() failed " + "for urb with rc:-%s\n", + strerror(rc)); + if (rc == -ENOSPC) + nospc++; + } else { + m++; + } + } else { + isbad++; + } + } + if (nospc) { + SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc); + SAM("..... possibly inadequate USB bandwidth\n"); + peasycap->video_eof = 1; + } + + if (isbad) { + JOM(4, "attempting cleanup instead of submitting\n"); + list_for_each(plist_head, (peasycap->purb_video_head)) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + purb = pdata_urb->purb; + if (purb) + usb_kill_urb(purb); + } + } + peasycap->video_isoc_streaming = 0; + } else { + peasycap->video_isoc_streaming = 1; + JOM(4, "submitted %i video urbs\n", m); + } + } else { + JOM(4, "already streaming video urbs\n"); + } + return 0; +} +/*****************************************************************************/ +int kill_video_urbs(struct easycap *peasycap) +{ + int m; + struct list_head *plist_head; + struct data_urb *pdata_urb; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->video_isoc_streaming) { + JOM(8, "%i=video_isoc_streaming, no video urbs killed\n", + peasycap->video_isoc_streaming); + return 0; + } + if (!peasycap->purb_video_head) { + SAM("ERROR: peasycap->purb_video_head is NULL\n"); + return -EFAULT; + } + + peasycap->video_isoc_streaming = 0; + JOM(4, "killing video urbs\n"); + m = 0; + list_for_each(plist_head, (peasycap->purb_video_head)) { + pdata_urb = list_entry(plist_head, struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + usb_kill_urb(pdata_urb->purb); + m++; + } + } + JOM(4, "%i video urbs killed\n", m); + + return 0; +} +/****************************************************************************/ +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +/*--------------------------------------------------------------------------*/ +static int easycap_open_noinode(struct file *file) +{ + return easycap_open(NULL, file); +} + +static int videodev_release(struct video_device *pvideo_device) +{ + struct easycap *peasycap; + + peasycap = video_get_drvdata(pvideo_device); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + SAY("ending unsuccessfully\n"); + return -EFAULT; + } + if (0 != kill_video_urbs(peasycap)) { + SAM("ERROR: kill_video_urbs() failed\n"); + return -EFAULT; + } + JOM(4, "ending successfully\n"); + return 0; +} +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ +/*****************************************************************************/ +/*--------------------------------------------------------------------------*/ +/* + * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS + * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect(). + * + * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO + * peasycap->pusb_device IS NO LONGER VALID. + */ +/*---------------------------------------------------------------------------*/ +static void easycap_delete(struct kref *pkref) +{ + struct easycap *peasycap; + struct data_urb *pdata_urb; + struct list_head *plist_head, *plist_next; + int k, m, gone, kd; + int allocation_video_urb; + int allocation_video_page; + int allocation_video_struct; + int allocation_audio_urb; + int allocation_audio_page; + int allocation_audio_struct; + int registered_video, registered_audio; + + peasycap = container_of(pkref, struct easycap, kref); + if (!peasycap) { + SAM("ERROR: peasycap is NULL: cannot perform deletions\n"); + return; + } + kd = isdongle(peasycap); +/*---------------------------------------------------------------------------*/ +/* + * FREE VIDEO. + */ +/*---------------------------------------------------------------------------*/ + if (peasycap->purb_video_head) { + JOM(4, "freeing video urbs\n"); + m = 0; + list_for_each(plist_head, (peasycap->purb_video_head)) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (!pdata_urb) { + JOM(4, "ERROR: pdata_urb is NULL\n"); + } else { + if (pdata_urb->purb) { + usb_free_urb(pdata_urb->purb); + pdata_urb->purb = NULL; + peasycap->allocation_video_urb -= 1; + m++; + } + } + } + + JOM(4, "%i video urbs freed\n", m); +/*---------------------------------------------------------------------------*/ + JOM(4, "freeing video data_urb structures.\n"); + m = 0; + list_for_each_safe(plist_head, plist_next, + peasycap->purb_video_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + peasycap->allocation_video_struct -= + sizeof(struct data_urb); + kfree(pdata_urb); + pdata_urb = NULL; + m++; + } + } + JOM(4, "%i video data_urb structures freed\n", m); + JOM(4, "setting peasycap->purb_video_head=NULL\n"); + peasycap->purb_video_head = NULL; + } +/*---------------------------------------------------------------------------*/ + JOM(4, "freeing video isoc buffers.\n"); + m = 0; + for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { + if (peasycap->video_isoc_buffer[k].pgo) { + free_pages((unsigned long) + peasycap->video_isoc_buffer[k].pgo, + VIDEO_ISOC_ORDER); + peasycap->video_isoc_buffer[k].pgo = NULL; + peasycap->allocation_video_page -= + BIT(VIDEO_ISOC_ORDER); + m++; + } + } + JOM(4, "isoc video buffers freed: %i pages\n", + m * (0x01 << VIDEO_ISOC_ORDER)); +/*---------------------------------------------------------------------------*/ + JOM(4, "freeing video field buffers.\n"); + gone = 0; + for (k = 0; k < FIELD_BUFFER_MANY; k++) { + for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->field_buffer[k][m].pgo) { + free_page((unsigned long) + peasycap->field_buffer[k][m].pgo); + peasycap->field_buffer[k][m].pgo = NULL; + peasycap->allocation_video_page -= 1; + gone++; + } + } + } + JOM(4, "video field buffers freed: %i pages\n", gone); +/*---------------------------------------------------------------------------*/ + JOM(4, "freeing video frame buffers.\n"); + gone = 0; + for (k = 0; k < FRAME_BUFFER_MANY; k++) { + for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->frame_buffer[k][m].pgo) { + free_page((unsigned long) + peasycap->frame_buffer[k][m].pgo); + peasycap->frame_buffer[k][m].pgo = NULL; + peasycap->allocation_video_page -= 1; + gone++; + } + } + } + JOM(4, "video frame buffers freed: %i pages\n", gone); +/*---------------------------------------------------------------------------*/ +/* + * FREE AUDIO. + */ +/*---------------------------------------------------------------------------*/ + if (peasycap->purb_audio_head) { + JOM(4, "freeing audio urbs\n"); + m = 0; + list_for_each(plist_head, (peasycap->purb_audio_head)) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (!pdata_urb) + JOM(4, "ERROR: pdata_urb is NULL\n"); + else { + if (pdata_urb->purb) { + usb_free_urb(pdata_urb->purb); + pdata_urb->purb = NULL; + peasycap->allocation_audio_urb -= 1; + m++; + } + } + } + JOM(4, "%i audio urbs freed\n", m); +/*---------------------------------------------------------------------------*/ + JOM(4, "freeing audio data_urb structures.\n"); + m = 0; + list_for_each_safe(plist_head, plist_next, + peasycap->purb_audio_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + peasycap->allocation_audio_struct -= + sizeof(struct data_urb); + kfree(pdata_urb); + pdata_urb = NULL; + m++; + } + } + JOM(4, "%i audio data_urb structures freed\n", m); + JOM(4, "setting peasycap->purb_audio_head=NULL\n"); + peasycap->purb_audio_head = NULL; + } +/*---------------------------------------------------------------------------*/ + JOM(4, "freeing audio isoc buffers.\n"); + m = 0; + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + if (peasycap->audio_isoc_buffer[k].pgo) { + free_pages((unsigned long) + (peasycap->audio_isoc_buffer[k].pgo), + AUDIO_ISOC_ORDER); + peasycap->audio_isoc_buffer[k].pgo = NULL; + peasycap->allocation_audio_page -= + BIT(AUDIO_ISOC_ORDER); + m++; + } + } + JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n", + m * (0x01 << AUDIO_ISOC_ORDER)); +/*---------------------------------------------------------------------------*/ + JOM(4, "freeing easycap structure.\n"); + allocation_video_urb = peasycap->allocation_video_urb; + allocation_video_page = peasycap->allocation_video_page; + allocation_video_struct = peasycap->allocation_video_struct; + registered_video = peasycap->registered_video; + allocation_audio_urb = peasycap->allocation_audio_urb; + allocation_audio_page = peasycap->allocation_audio_page; + allocation_audio_struct = peasycap->allocation_audio_struct; + registered_audio = peasycap->registered_audio; + + if (0 <= kd && DONGLE_MANY > kd) { + if (mutex_lock_interruptible(&mutex_dongle)) { + SAY("ERROR: cannot down mutex_dongle\n"); + } else { + JOM(4, "locked mutex_dongle\n"); + easycapdc60_dongle[kd].peasycap = NULL; + mutex_unlock(&mutex_dongle); + JOM(4, "unlocked mutex_dongle\n"); + JOT(4, " null-->dongle[%i].peasycap\n", kd); + allocation_video_struct -= sizeof(struct easycap); + } + } else { + SAY("ERROR: cannot purge dongle[].peasycap"); + } + + kfree(peasycap); + +/*---------------------------------------------------------------------------*/ + SAY("%8i=video urbs after all deletions\n", allocation_video_urb); + SAY("%8i=video pages after all deletions\n", allocation_video_page); + SAY("%8i=video structs after all deletions\n", allocation_video_struct); + SAY("%8i=video devices after all deletions\n", registered_video); + SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb); + SAY("%8i=audio pages after all deletions\n", allocation_audio_page); + SAY("%8i=audio structs after all deletions\n", allocation_audio_struct); + SAY("%8i=audio devices after all deletions\n", registered_audio); + + JOT(4, "ending.\n"); + return; +} +/*****************************************************************************/ +static unsigned int easycap_poll(struct file *file, poll_table *wait) +{ + struct easycap *peasycap; + int rc, kd; + + JOT(8, "\n"); + + if (NULL == ((poll_table *)wait)) + JOT(8, "WARNING: poll table pointer is NULL ... continuing\n"); + if (!file) { + SAY("ERROR: file pointer is NULL\n"); + return -ERESTARTSYS; + } + peasycap = file->private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAY("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } +/*---------------------------------------------------------------------------*/ + kd = isdongle(peasycap); + if (0 <= kd && DONGLE_MANY > kd) { + if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { + SAY("ERROR: cannot down dongle[%i].mutex_video\n", kd); + return -ERESTARTSYS; + } + JOM(4, "locked dongle[%i].mutex_video\n", kd); + /* + * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER + * peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL. + * IF NECESSARY, BAIL OUT. + */ + if (kd != isdongle(peasycap)) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ERESTARTSYS; + } + if (!file) { + SAY("ERROR: file is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ERESTARTSYS; + } + peasycap = file->private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ERESTARTSYS; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return -ERESTARTSYS; + } + } else + /* + * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap + * BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL + * HAVE FAILED. BAIL OUT. + */ + return -ERESTARTSYS; +/*---------------------------------------------------------------------------*/ + rc = easycap_dqbuf(peasycap, 0); + peasycap->polled = 1; + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + if (0 == rc) + return POLLIN | POLLRDNORM; + else + return POLLERR; + } +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * IF mode IS NONZERO THIS ROUTINE RETURNS -EAGAIN RATHER THAN BLOCKING. + */ +/*---------------------------------------------------------------------------*/ +int easycap_dqbuf(struct easycap *peasycap, int mode) +{ + int input, ifield, miss, rc; + + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAY("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + ifield = 0; + JOM(8, "%i=ifield\n", ifield); +/*---------------------------------------------------------------------------*/ +/* + * CHECK FOR LOST INPUT SIGNAL. + * + * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED. + * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT + * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE + * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS: + * + * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK + * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK + * INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK + * INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS +*/ +/*---------------------------------------------------------------------------*/ + input = peasycap->input; + if (0 <= input && INPUT_MANY > input) { + rc = read_saa(peasycap->pusb_device, 0x1F); + if (0 <= rc) { + if (rc & 0x40) + peasycap->lost[input] += 1; + else + peasycap->lost[input] -= 2; + + if (0 > peasycap->lost[input]) + peasycap->lost[input] = 0; + else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input]) + peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE); + } + } +/*---------------------------------------------------------------------------*/ +/* + * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM) + */ +/*---------------------------------------------------------------------------*/ + miss = 0; + while ((peasycap->field_read == peasycap->field_fill) || + (0 != (0xFF00 & peasycap->field_buffer + [peasycap->field_read][0].kount)) || + (ifield != (0x00FF & peasycap->field_buffer + [peasycap->field_read][0].kount))) { + if (mode) + return -EAGAIN; + + JOM(8, "first wait on wq_video, %i=field_read %i=field_fill\n", + peasycap->field_read, peasycap->field_fill); + + if (0 != (wait_event_interruptible(peasycap->wq_video, + (peasycap->video_idle || peasycap->video_eof || + ((peasycap->field_read != peasycap->field_fill) && + (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) && + (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) { + SAM("aborted by signal\n"); + return -EIO; + } + if (peasycap->video_idle) { + JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n", + peasycap->video_idle); + return -EAGAIN; + } + if (peasycap->video_eof) { + JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof); + #if defined(PERSEVERE) + if (1 == peasycap->status) { + JOM(8, "persevering ...\n"); + peasycap->video_eof = 0; + peasycap->audio_eof = 0; + if (0 != reset(peasycap)) { + JOM(8, " ... failed returning -EIO\n"); + peasycap->video_eof = 1; + peasycap->audio_eof = 1; + kill_video_urbs(peasycap); + return -EIO; + } + peasycap->status = 0; + JOM(8, " ... OK returning -EAGAIN\n"); + return -EAGAIN; + } + #endif /*PERSEVERE*/ + peasycap->video_eof = 1; + peasycap->audio_eof = 1; + kill_video_urbs(peasycap); + JOM(8, "returning -EIO\n"); + return -EIO; + } + miss++; + } + JOM(8, "first awakening on wq_video after %i waits\n", miss); + + rc = field2frame(peasycap); + if (rc) + SAM("ERROR: field2frame() rc = %i\n", rc); +/*---------------------------------------------------------------------------*/ +/* + * WAIT FOR THE OTHER FIELD + */ +/*---------------------------------------------------------------------------*/ + if (ifield) + ifield = 0; + else + ifield = 1; + miss = 0; + while ((peasycap->field_read == peasycap->field_fill) || + (0 != (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) || + (ifield != (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))) { + if (mode) + return -EAGAIN; + + JOM(8, "second wait on wq_video %i=field_read %i=field_fill\n", + peasycap->field_read, peasycap->field_fill); + if (0 != (wait_event_interruptible(peasycap->wq_video, + (peasycap->video_idle || peasycap->video_eof || + ((peasycap->field_read != peasycap->field_fill) && + (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) && + (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) { + SAM("aborted by signal\n"); + return -EIO; + } + if (peasycap->video_idle) { + JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n", + peasycap->video_idle); + return -EAGAIN; + } + if (peasycap->video_eof) { + JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof); +#if defined(PERSEVERE) + if (1 == peasycap->status) { + JOM(8, "persevering ...\n"); + peasycap->video_eof = 0; + peasycap->audio_eof = 0; + if (0 != reset(peasycap)) { + JOM(8, " ... failed returning -EIO\n"); + peasycap->video_eof = 1; + peasycap->audio_eof = 1; + kill_video_urbs(peasycap); + return -EIO; + } + peasycap->status = 0; + JOM(8, " ... OK ... returning -EAGAIN\n"); + return -EAGAIN; + } +#endif /*PERSEVERE*/ + peasycap->video_eof = 1; + peasycap->audio_eof = 1; + kill_video_urbs(peasycap); + JOM(8, "returning -EIO\n"); + return -EIO; + } + miss++; + } + JOM(8, "second awakening on wq_video after %i waits\n", miss); + + rc = field2frame(peasycap); + if (rc) + SAM("ERROR: field2frame() rc = %i\n", rc); +/*---------------------------------------------------------------------------*/ +/* + * WASTE THIS FRAME +*/ +/*---------------------------------------------------------------------------*/ + if (peasycap->skip) { + peasycap->skipped++; + if (peasycap->skip != peasycap->skipped) + return peasycap->skip - peasycap->skipped; + else + peasycap->skipped = 0; + } +/*---------------------------------------------------------------------------*/ + peasycap->frame_read = peasycap->frame_fill; + peasycap->queued[peasycap->frame_read] = 0; + peasycap->done[peasycap->frame_read] = V4L2_BUF_FLAG_DONE; + + peasycap->frame_fill++; + if (peasycap->frame_buffer_many <= peasycap->frame_fill) + peasycap->frame_fill = 0; + + if (0x01 & easycap_standard[peasycap->standard_offset].mask) + peasycap->frame_buffer[peasycap->frame_read][0].kount = + V4L2_FIELD_TOP; + else + peasycap->frame_buffer[peasycap->frame_read][0].kount = + V4L2_FIELD_BOTTOM; + + + JOM(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read); + JOM(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill); + + return 0; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * BY DEFINITION, odd IS true FOR THE FIELD OCCUPYING LINES 1,3,5,...,479 + * odd IS false FOR THE FIELD OCCUPYING LINES 0,2,4,...,478 + * + * WHEN BOOLEAN PARAMETER decimatepixel IS true, ONLY THE FIELD FOR WHICH + * odd==false IS TRANSFERRED TO THE FRAME BUFFER. + * + * THE BOOLEAN PARAMETER offerfields IS true ONLY WHEN THE USER PROGRAM + * CHOOSES THE OPTION V4L2_FIELD_INTERLACED. + */ +/*---------------------------------------------------------------------------*/ +int +field2frame(struct easycap *peasycap) +{ + + void *pex, *pad; + int kex, kad, mex, mad, rex, rad, rad2; + int c2, c3, w2, w3, cz, wz; + int rc, bytesperpixel, multiplier; + int much, more, over, rump, caches, input; + u8 mask, margin; + bool odd, isuy, decimatepixel, offerfields, badinput; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + + badinput = false; + input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input; + + JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> " + "frame buffer %i\n", + peasycap->field_buffer[peasycap->field_read][0].kount, + peasycap->field_buffer[peasycap->field_read][0].input, + peasycap->field_read, peasycap->frame_fill); + JOM(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel); + if (peasycap->offerfields) + JOM(8, "===== offerfields\n"); + +/*---------------------------------------------------------------------------*/ +/* + * REJECT OR CLEAN BAD FIELDS + */ +/*---------------------------------------------------------------------------*/ + if (peasycap->field_read == peasycap->field_fill) { + SAM("ERROR: on entry, still filling field buffer %i\n", + peasycap->field_read); + return 0; + } +#ifdef EASYCAP_TESTCARD + easycap_testcard(peasycap, peasycap->field_read); +#else + if (0 <= input && INPUT_MANY > input) { + if (easycap_bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input]) + easycap_testcard(peasycap, peasycap->field_read); + } +#endif /*EASYCAP_TESTCARD*/ +/*---------------------------------------------------------------------------*/ + + offerfields = peasycap->offerfields; + bytesperpixel = peasycap->bytesperpixel; + decimatepixel = peasycap->decimatepixel; + + if ((2 != bytesperpixel) && + (3 != bytesperpixel) && + (4 != bytesperpixel)) { + SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel); + return -EFAULT; + } + if (decimatepixel) + multiplier = 2; + else + multiplier = 1; + + w2 = 2 * multiplier * (peasycap->width); + w3 = bytesperpixel * multiplier * (peasycap->width); + wz = multiplier * (peasycap->height) * + multiplier * (peasycap->width); + + kex = peasycap->field_read; mex = 0; + kad = peasycap->frame_fill; mad = 0; + + pex = peasycap->field_buffer[kex][0].pgo; rex = PAGE_SIZE; + pad = peasycap->frame_buffer[kad][0].pgo; rad = PAGE_SIZE; + odd = !!(peasycap->field_buffer[kex][0].kount); + + if (odd && (!decimatepixel)) { + JOM(8, "initial skipping %4i bytes p.%4i\n", + w3/multiplier, mad); + pad += (w3 / multiplier); rad -= (w3 / multiplier); + } + isuy = true; + mask = 0; rump = 0; caches = 0; + + cz = 0; + while (cz < wz) { + /* + * PROCESS ONE LINE OF FRAME AT FULL RESOLUTION: + * READ w2 BYTES FROM FIELD BUFFER, + * WRITE w3 BYTES TO FRAME BUFFER + */ + if (!decimatepixel) { + over = w2; + do { + much = over; more = 0; + margin = 0; mask = 0x00; + if (rex < much) + much = rex; + rump = 0; + + if (much % 2) { + SAM("MISTAKE: much is odd\n"); + return -EFAULT; + } + + more = (bytesperpixel * + much) / 2; +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + if (1 < bytesperpixel) { + if (rad * 2 < much * bytesperpixel) { + /* + * INJUDICIOUS ALTERATION OF + * THIS STATEMENT BLOCK WILL + * CAUSE BREAKAGE. BEWARE. + */ + rad2 = rad + bytesperpixel - 1; + much = ((((2 * rad2)/bytesperpixel)/2) * 2); + rump = ((bytesperpixel * much) / 2) - rad; + more = rad; + } + mask = (u8)rump; + margin = 0; + if (much == rex) { + mask |= 0x04; + if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE) + margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo)); + else + mask |= 0x08; + } + } else { + SAM("MISTAKE: %i=bytesperpixel\n", + bytesperpixel); + return -EFAULT; + } + if (rump) + caches++; + if (badinput) { + JOM(8, "ERROR: 0x%02X=->field_buffer" + "[%i][%i].input, " + "0x%02X=(0x08|->input)\n", + peasycap->field_buffer + [kex][mex].input, kex, mex, + (0x08|peasycap->input)); + } + rc = redaub(peasycap, pad, pex, much, more, + mask, margin, isuy); + if (0 > rc) { + SAM("ERROR: redaub() failed\n"); + return -EFAULT; + } + if (much % 4) + isuy = !isuy; + + over -= much; cz += much; + pex += much; rex -= much; + if (!rex) { + mex++; + pex = peasycap->field_buffer[kex][mex].pgo; + rex = PAGE_SIZE; + if (peasycap->field_buffer[kex][mex].input != (0x08|peasycap->input)) + badinput = true; + } + pad += more; + rad -= more; + if (!rad) { + mad++; + pad = peasycap->frame_buffer[kad][mad].pgo; + rad = PAGE_SIZE; + if (rump) { + pad += rump; + rad -= rump; + } + } + } while (over); +/*---------------------------------------------------------------------------*/ +/* + * SKIP w3 BYTES IN TARGET FRAME BUFFER, + * UNLESS IT IS THE LAST LINE OF AN ODD FRAME + */ +/*---------------------------------------------------------------------------*/ + if (!odd || (cz != wz)) { + over = w3; + do { + if (!rad) { + mad++; + pad = peasycap->frame_buffer + [kad][mad].pgo; + rad = PAGE_SIZE; + } + more = over; + if (rad < more) + more = rad; + over -= more; + pad += more; + rad -= more; + } while (over); + } +/*---------------------------------------------------------------------------*/ +/* + * PROCESS ONE LINE OF FRAME AT REDUCED RESOLUTION: + * ONLY IF false==odd, + * READ w2 BYTES FROM FIELD BUFFER, + * WRITE w3 / 2 BYTES TO FRAME BUFFER + */ +/*---------------------------------------------------------------------------*/ + } else if (!odd) { + over = w2; + do { + much = over; more = 0; margin = 0; mask = 0x00; + if (rex < much) + much = rex; + rump = 0; + + if (much % 2) { + SAM("MISTAKE: much is odd\n"); + return -EFAULT; + } + + more = (bytesperpixel * much) / 4; +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + if (1 < bytesperpixel) { + if (rad * 4 < much * bytesperpixel) { + /* + * INJUDICIOUS ALTERATION OF + * THIS STATEMENT BLOCK + * WILL CAUSE BREAKAGE. + * BEWARE. + */ + rad2 = rad + bytesperpixel - 1; + much = ((((2 * rad2) / bytesperpixel) / 2) * 4); + rump = ((bytesperpixel * much) / 4) - rad; + more = rad; + } + mask = (u8)rump; + margin = 0; + if (much == rex) { + mask |= 0x04; + if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE) + margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo)); + else + mask |= 0x08; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + } else { + SAM("MISTAKE: %i=bytesperpixel\n", + bytesperpixel); + return -EFAULT; + } +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + if (rump) + caches++; + + if (badinput) { + JOM(8, "ERROR: 0x%02X=->field_buffer" + "[%i][%i].input, " + "0x%02X=(0x08|->input)\n", + peasycap->field_buffer + [kex][mex].input, kex, mex, + (0x08|peasycap->input)); + } + rc = redaub(peasycap, pad, pex, much, more, + mask, margin, isuy); + if (0 > rc) { + SAM("ERROR: redaub() failed\n"); + return -EFAULT; + } + over -= much; cz += much; + pex += much; rex -= much; + if (!rex) { + mex++; + pex = peasycap->field_buffer[kex][mex].pgo; + rex = PAGE_SIZE; + if (peasycap->field_buffer[kex][mex].input != + (0x08|peasycap->input)) + badinput = true; + } + pad += more; + rad -= more; + if (!rad) { + mad++; + pad = peasycap->frame_buffer[kad][mad].pgo; + rad = PAGE_SIZE; + if (rump) { + pad += rump; + rad -= rump; + } + } + } while (over); +/*---------------------------------------------------------------------------*/ +/* + * OTHERWISE JUST + * READ w2 BYTES FROM FIELD BUFFER AND DISCARD THEM + */ +/*---------------------------------------------------------------------------*/ + } else { + over = w2; + do { + if (!rex) { + mex++; + pex = peasycap->field_buffer[kex][mex].pgo; + rex = PAGE_SIZE; + if (peasycap->field_buffer[kex][mex].input != + (0x08|peasycap->input)) { + JOM(8, "ERROR: 0x%02X=->field_buffer" + "[%i][%i].input, " + "0x%02X=(0x08|->input)\n", + peasycap->field_buffer + [kex][mex].input, kex, mex, + (0x08|peasycap->input)); + badinput = true; + } + } + much = over; + if (rex < much) + much = rex; + over -= much; + cz += much; + pex += much; + rex -= much; + } while (over); + } + } +/*---------------------------------------------------------------------------*/ +/* + * SANITY CHECKS + */ +/*---------------------------------------------------------------------------*/ + c2 = (mex + 1)*PAGE_SIZE - rex; + if (cz != c2) + SAM("ERROR: discrepancy %i in bytes read\n", c2 - cz); + c3 = (mad + 1)*PAGE_SIZE - rad; + + if (!decimatepixel) { + if (bytesperpixel * cz != c3) + SAM("ERROR: discrepancy %i in bytes written\n", + c3 - (bytesperpixel * cz)); + } else { + if (!odd) { + if (bytesperpixel * + cz != (4 * c3)) + SAM("ERROR: discrepancy %i in bytes written\n", + (2*c3)-(bytesperpixel * cz)); + } else { + if (0 != c3) + SAM("ERROR: discrepancy %i " + "in bytes written\n", c3); + } + } + if (rump) + SAM("WORRY: undischarged cache at end of line in frame buffer\n"); + + JOM(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3); + JOM(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad); + + if (odd) + JOM(8, "+++++ field2frame(): frame buffer %i is full\n", kad); + + if (peasycap->field_read == peasycap->field_fill) + SAM("WARNING: on exit, filling field buffer %i\n", + peasycap->field_read); + + if (caches) + JOM(8, "%i=caches\n", caches); + return 0; +} +/*---------------------------------------------------------------------------*/ +/* + * DECIMATION AND COLOURSPACE CONVERSION. + * + * THIS ROUTINE REQUIRES THAT ALL THE DATA TO BE READ RESIDES ON ONE PAGE + * AND THAT ALL THE DATA TO BE WRITTEN RESIDES ON ONE (DIFFERENT) PAGE. + * THE CALLING ROUTINE MUST ENSURE THAT THIS REQUIREMENT IS MET, AND MUST + * ALSO ENSURE THAT much IS EVEN. + * + * much BYTES ARE READ, AT LEAST (bytesperpixel * much)/2 BYTES ARE WRITTEN + * IF THERE IS NO DECIMATION, HALF THIS AMOUNT IF THERE IS DECIMATION. + * + * mask IS ZERO WHEN NO SPECIAL BEHAVIOUR REQUIRED. OTHERWISE IT IS SET THUS: + * 0x03 & mask = number of bytes to be written to cache instead of to + * frame buffer + * 0x04 & mask => use argument margin to set the chrominance for last pixel + * 0x08 & mask => do not set the chrominance for last pixel + * + * YUV to RGB CONVERSION IS (OR SHOULD BE) ITU-R BT 601. + * + * THERE IS A LOT OF CODE REPETITION IN THIS ROUTINE IN ORDER TO AVOID + * INEFFICIENT SWITCHING INSIDE INNER LOOPS. REARRANGING THE LOGIC TO + * REDUCE CODE LENGTH WILL GENERALLY IMPAIR RUNTIME PERFORMANCE. BEWARE. + */ +/*---------------------------------------------------------------------------*/ +int +redaub(struct easycap *peasycap, void *pad, void *pex, int much, int more, + u8 mask, u8 margin, bool isuy) +{ + static s32 ay[256], bu[256], rv[256], gu[256], gv[256]; + u8 *pcache; + u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr; + int bytesperpixel; + bool byteswaporder, decimatepixel, last; + int j, rump; + s32 tmp; + + if (much % 2) { + SAM("MISTAKE: much is odd\n"); + return -EFAULT; + } + bytesperpixel = peasycap->bytesperpixel; + byteswaporder = peasycap->byteswaporder; + decimatepixel = peasycap->decimatepixel; + +/*---------------------------------------------------------------------------*/ + if (!bu[255]) { + for (j = 0; j < 112; j++) { + tmp = (0xFF00 & (453 * j)) >> 8; + bu[j + 128] = tmp; bu[127 - j] = -tmp; + tmp = (0xFF00 & (359 * j)) >> 8; + rv[j + 128] = tmp; rv[127 - j] = -tmp; + tmp = (0xFF00 & (88 * j)) >> 8; + gu[j + 128] = tmp; gu[127 - j] = -tmp; + tmp = (0xFF00 & (183 * j)) >> 8; + gv[j + 128] = tmp; gv[127 - j] = -tmp; + } + for (j = 0; j < 16; j++) { + bu[j] = bu[16]; rv[j] = rv[16]; + gu[j] = gu[16]; gv[j] = gv[16]; + } + for (j = 240; j < 256; j++) { + bu[j] = bu[239]; rv[j] = rv[239]; + gu[j] = gu[239]; gv[j] = gv[239]; + } + for (j = 16; j < 236; j++) + ay[j] = j; + for (j = 0; j < 16; j++) + ay[j] = ay[16]; + for (j = 236; j < 256; j++) + ay[j] = ay[235]; + JOM(8, "lookup tables are prepared\n"); + } + pcache = peasycap->pcache; + if (!pcache) + pcache = &peasycap->cache[0]; +/*---------------------------------------------------------------------------*/ +/* + * TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER + */ +/*---------------------------------------------------------------------------*/ + if (!pcache) { + SAM("MISTAKE: pcache is NULL\n"); + return -EFAULT; + } + + if (pcache != &peasycap->cache[0]) + JOM(16, "cache has %i bytes\n", (int)(pcache - &peasycap->cache[0])); + p2 = &peasycap->cache[0]; + p3 = (u8 *)pad - (int)(pcache - &peasycap->cache[0]); + while (p2 < pcache) { + *p3++ = *p2; p2++; + } + pcache = &peasycap->cache[0]; + if (p3 != pad) { + SAM("MISTAKE: pointer misalignment\n"); + return -EFAULT; + } +/*---------------------------------------------------------------------------*/ + rump = (int)(0x03 & mask); + u = 0; v = 0; + p2 = (u8 *)pex; pz = p2 + much; pr = p3 + more; last = false; + p2++; + + if (isuy) + u = *(p2 - 1); + else + v = *(p2 - 1); + + if (rump) + JOM(16, "%4i=much %4i=more %i=rump\n", much, more, rump); + +/*---------------------------------------------------------------------------*/ + switch (bytesperpixel) { + case 2: { + if (!decimatepixel) { + memcpy(pad, pex, (size_t)much); + if (!byteswaporder) { + /* UYVY */ + return 0; + } else { + /* YUYV */ + p3 = (u8 *)pad; pz = p3 + much; + while (pz > p3) { + c = *p3; + *p3 = *(p3 + 1); + *(p3 + 1) = c; + p3 += 2; + } + return 0; + } + } else { + if (!byteswaporder) { + /* UYVY DECIMATED */ + p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much; + while (pz > p2) { + *p3 = *p2; + *(p3 + 1) = *(p2 + 1); + *(p3 + 2) = *(p2 + 2); + *(p3 + 3) = *(p2 + 3); + p3 += 4; p2 += 8; + } + return 0; + } else { + /* YUYV DECIMATED */ + p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much; + while (pz > p2) { + *p3 = *(p2 + 1); + *(p3 + 1) = *p2; + *(p3 + 2) = *(p2 + 3); + *(p3 + 3) = *(p2 + 2); + p3 += 4; p2 += 8; + } + return 0; + } + } + break; + } + case 3: + { + if (!decimatepixel) { + if (!byteswaporder) { + /* RGB */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = r; + *pcache++ = g; + *pcache++ = b; + break; + } + case 2: { + *p3 = r; + *(p3 + 1) = g; + *pcache++ = b; + break; + } + default: { + SAM("MISTAKE: %i=rump\n", + bytesperpixel - rump); + return -EFAULT; + } + } + } else { + *p3 = r; + *(p3 + 1) = g; + *(p3 + 2) = b; + } + p2 += 2; + if (isuy) + isuy = false; + else + isuy = true; + p3 += bytesperpixel; + } + return 0; + } else { + /* BGR */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } + else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = b; + *pcache++ = g; + *pcache++ = r; + break; + } + case 2: { + *p3 = b; + *(p3 + 1) = g; + *pcache++ = r; + break; + } + default: { + SAM("MISTAKE: %i=rump\n", + bytesperpixel - rump); + return -EFAULT; + } + } + } else { + *p3 = b; + *(p3 + 1) = g; + *(p3 + 2) = r; + } + p2 += 2; + if (isuy) + isuy = false; + else + isuy = true; + p3 += bytesperpixel; + } + } + return 0; + } else { + if (!byteswaporder) { + /* RGB DECIMATED */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + if (isuy) { + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - + gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = r; + *pcache++ = g; + *pcache++ = b; + break; + } + case 2: { + *p3 = r; + *(p3 + 1) = g; + *pcache++ = b; + break; + } + default: { + SAM("MISTAKE: " + "%i=rump\n", + bytesperpixel - rump); + return -EFAULT; + } + } + } else { + *p3 = r; + *(p3 + 1) = g; + *(p3 + 2) = b; + } + isuy = false; + p3 += bytesperpixel; + } else { + isuy = true; + } + p2 += 2; + } + return 0; + } else { + /* BGR DECIMATED */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + if (isuy) { + + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - + gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = b; + *pcache++ = g; + *pcache++ = r; + break; + } + case 2: { + *p3 = b; + *(p3 + 1) = g; + *pcache++ = r; + break; + } + default: { + SAM("MISTAKE: " + "%i=rump\n", + bytesperpixel - rump); + return -EFAULT; + } + } + } else { + *p3 = b; + *(p3 + 1) = g; + *(p3 + 2) = r; + } + isuy = false; + p3 += bytesperpixel; + } + else + isuy = true; + p2 += 2; + } + return 0; + } + } + break; + } + case 4: + { + if (!decimatepixel) { + if (!byteswaporder) { + /* RGBA */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = r; + *pcache++ = g; + *pcache++ = b; + *pcache++ = 0; + break; + } + case 2: { + *p3 = r; + *(p3 + 1) = g; + *pcache++ = b; + *pcache++ = 0; + break; + } + case 3: { + *p3 = r; + *(p3 + 1) = g; + *(p3 + 2) = b; + *pcache++ = 0; + break; + } + default: { + SAM("MISTAKE: %i=rump\n", + bytesperpixel - rump); + return -EFAULT; + } + } + } else { + *p3 = r; + *(p3 + 1) = g; + *(p3 + 2) = b; + *(p3 + 3) = 0; + } + p2 += 2; + if (isuy) + isuy = false; + else + isuy = true; + p3 += bytesperpixel; + } + return 0; + } else { + /* + * BGRA + */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = b; + *pcache++ = g; + *pcache++ = r; + *pcache++ = 0; + break; + } + case 2: { + *p3 = b; + *(p3 + 1) = g; + *pcache++ = r; + *pcache++ = 0; + break; + } + case 3: { + *p3 = b; + *(p3 + 1) = g; + *(p3 + 2) = r; + *pcache++ = 0; + break; + } + default: + SAM("MISTAKE: %i=rump\n", + bytesperpixel - rump); + return -EFAULT; + } + } else { + *p3 = b; + *(p3 + 1) = g; + *(p3 + 2) = r; + *(p3 + 3) = 0; + } + p2 += 2; + if (isuy) + isuy = false; + else + isuy = true; + p3 += bytesperpixel; + } + } + return 0; + } else { + if (!byteswaporder) { + /* + * RGBA DECIMATED + */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + if (isuy) { + + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - + gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = r; + *pcache++ = g; + *pcache++ = b; + *pcache++ = 0; + break; + } + case 2: { + *p3 = r; + *(p3 + 1) = g; + *pcache++ = b; + *pcache++ = 0; + break; + } + case 3: { + *p3 = r; + *(p3 + 1) = g; + *(p3 + 2) = b; + *pcache++ = 0; + break; + } + default: { + SAM("MISTAKE: " + "%i=rump\n", + bytesperpixel - + rump); + return -EFAULT; + } + } + } else { + *p3 = r; + *(p3 + 1) = g; + *(p3 + 2) = b; + *(p3 + 3) = 0; + } + isuy = false; + p3 += bytesperpixel; + } else + isuy = true; + p2 += 2; + } + return 0; + } else { + /* + * BGRA DECIMATED + */ + while (pz > p2) { + if (pr <= (p3 + bytesperpixel)) + last = true; + else + last = false; + y = *p2; + if (last && (0x0C & mask)) { + if (0x04 & mask) { + if (isuy) + v = margin; + else + u = margin; + } else + if (0x08 & mask) + ; + } else { + if (isuy) + v = *(p2 + 1); + else + u = *(p2 + 1); + } + + if (isuy) { + tmp = ay[(int)y] + rv[(int)v]; + r = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] - gu[(int)u] - + gv[(int)v]; + g = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + tmp = ay[(int)y] + bu[(int)u]; + b = (255 < tmp) ? 255 : ((0 > tmp) ? + 0 : (u8)tmp); + + if (last && rump) { + pcache = &peasycap->cache[0]; + switch (bytesperpixel - rump) { + case 1: { + *p3 = b; + *pcache++ = g; + *pcache++ = r; + *pcache++ = 0; + break; + } + case 2: { + *p3 = b; + *(p3 + 1) = g; + *pcache++ = r; + *pcache++ = 0; + break; + } + case 3: { + *p3 = b; + *(p3 + 1) = g; + *(p3 + 2) = r; + *pcache++ = 0; + break; + } + default: { + SAM("MISTAKE: " + "%i=rump\n", + bytesperpixel - rump); + return -EFAULT; + } + } + } else { + *p3 = b; + *(p3 + 1) = g; + *(p3 + 2) = r; + *(p3 + 3) = 0; + } + isuy = false; + p3 += bytesperpixel; + } else + isuy = true; + p2 += 2; + } + return 0; + } + } + break; + } + default: { + SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel); + return -EFAULT; + } + } + return 0; +} +/*****************************************************************************/ +/* + * SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434 + */ +/*****************************************************************************/ +static void easycap_vma_open(struct vm_area_struct *pvma) +{ + struct easycap *peasycap; + + peasycap = pvma->vm_private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return; + } + peasycap->vma_many++; + JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many); + return; +} +/*****************************************************************************/ +static void easycap_vma_close(struct vm_area_struct *pvma) +{ + struct easycap *peasycap; + + peasycap = pvma->vm_private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return; + } + peasycap->vma_many--; + JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many); + return; +} +/*****************************************************************************/ +static int easycap_vma_fault(struct vm_area_struct *pvma, struct vm_fault *pvmf) +{ + int k, m, retcode; + void *pbuf; + struct page *page; + struct easycap *peasycap; + + retcode = VM_FAULT_NOPAGE; + + if (!pvma) { + SAY("pvma is NULL\n"); + return retcode; + } + if (!pvmf) { + SAY("pvmf is NULL\n"); + return retcode; + } + + k = (pvmf->pgoff) / (FRAME_BUFFER_SIZE/PAGE_SIZE); + m = (pvmf->pgoff) % (FRAME_BUFFER_SIZE/PAGE_SIZE); + + if (!m) + JOT(4, "%4i=k, %4i=m\n", k, m); + else + JOT(16, "%4i=k, %4i=m\n", k, m); + + if ((0 > k) || (FRAME_BUFFER_MANY <= k)) { + SAY("ERROR: buffer index %i out of range\n", k); + return retcode; + } + if ((0 > m) || (FRAME_BUFFER_SIZE/PAGE_SIZE <= m)) { + SAY("ERROR: page number %i out of range\n", m); + return retcode; + } + peasycap = pvma->vm_private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return retcode; + } +/*---------------------------------------------------------------------------*/ + pbuf = peasycap->frame_buffer[k][m].pgo; + if (!pbuf) { + SAM("ERROR: pbuf is NULL\n"); + return retcode; + } + page = virt_to_page(pbuf); + if (!page) { + SAM("ERROR: page is NULL\n"); + return retcode; + } + get_page(page); +/*---------------------------------------------------------------------------*/ + if (!page) { + SAM("ERROR: page is NULL after get_page(page)\n"); + } else { + pvmf->page = page; + retcode = VM_FAULT_MINOR; + } + return retcode; +} + +static const struct vm_operations_struct easycap_vm_ops = { + .open = easycap_vma_open, + .close = easycap_vma_close, + .fault = easycap_vma_fault, +}; + +static int easycap_mmap(struct file *file, struct vm_area_struct *pvma) +{ + JOT(8, "\n"); + + pvma->vm_ops = &easycap_vm_ops; + pvma->vm_flags |= VM_RESERVED; + if (file) + pvma->vm_private_data = file->private_data; + easycap_vma_open(pvma); + return 0; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS + * PROVIDED peasycap->video_idle IS ZERO. REGARDLESS OF THIS BEING TRUE, + * IT IS RESUBMITTED PROVIDED peasycap->video_isoc_streaming IS NOT ZERO. + * + * THIS FUNCTION IS AN INTERRUPT SERVICE ROUTINE AND MUST NOT SLEEP. + * + * INFORMATION ABOUT THE VALIDITY OF THE CONTENTS OF THE FIELD BUFFER ARE + * STORED IN THE TWO-BYTE STATUS PARAMETER + * peasycap->field_buffer[peasycap->field_fill][0].kount + * NOTICE THAT THE INFORMATION IS STORED ONLY WITH PAGE 0 OF THE FIELD BUFFER. + * + * THE LOWER BYTE CONTAINS THE FIELD PARITY BYTE FURNISHED BY THE SAA7113H + * CHIP. + * + * THE UPPER BYTE IS ZERO IF NO PROBLEMS, OTHERWISE: + * 0 != (kount & 0x8000) => AT LEAST ONE URB COMPLETED WITH ERRORS + * 0 != (kount & 0x4000) => BUFFER HAS TOO MUCH DATA + * 0 != (kount & 0x2000) => BUFFER HAS NOT ENOUGH DATA + * 0 != (kount & 0x1000) => BUFFER HAS DATA FROM DISPARATE INPUTS + * 0 != (kount & 0x0400) => RESERVED + * 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED + * 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY? + */ +/*---------------------------------------------------------------------------*/ +static void easycap_complete(struct urb *purb) +{ + struct easycap *peasycap; + struct data_buffer *pfield_buffer; + char errbuf[16]; + int i, more, much, leap, rc, last; + int videofieldamount; + unsigned int override, bad; + int framestatus, framelength, frameactual, frameoffset; + u8 *pu; + + if (!purb) { + SAY("ERROR: easycap_complete(): purb is NULL\n"); + return; + } + peasycap = purb->context; + if (!peasycap) { + SAY("ERROR: easycap_complete(): peasycap is NULL\n"); + return; + } + if (peasycap->video_eof) + return; + for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++) + if (purb->transfer_buffer == peasycap->video_isoc_buffer[i].pgo) + break; + JOM(16, "%2i=urb\n", i); + last = peasycap->video_isoc_sequence; + if ((((VIDEO_ISOC_BUFFER_MANY - 1) == last) && (0 != i)) || + (((VIDEO_ISOC_BUFFER_MANY - 1) != last) && ((last + 1) != i))) { + JOM(16, "ERROR: out-of-order urbs %i,%i ... continuing\n", + last, i); + } + peasycap->video_isoc_sequence = i; + + if (peasycap->video_idle) { + JOM(16, "%i=video_idle %i=video_isoc_streaming\n", + peasycap->video_idle, peasycap->video_isoc_streaming); + if (peasycap->video_isoc_streaming) { + rc = usb_submit_urb(purb, GFP_ATOMIC); + if (rc) { + SAM("%s:%d ENOMEM\n", strerror(rc), rc); + if (-ENODEV != rc) + SAM("ERROR: while %i=video_idle, " + "usb_submit_urb() " + "failed with rc:\n", + peasycap->video_idle); + } + } + return; + } + override = 0; +/*---------------------------------------------------------------------------*/ + if (FIELD_BUFFER_MANY <= peasycap->field_fill) { + SAM("ERROR: bad peasycap->field_fill\n"); + return; + } + if (purb->status) { + if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) { + JOM(8, "urb status -ESHUTDOWN or -ENOENT\n"); + return; + } + + (peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ; + SAM("ERROR: bad urb status -%s: %d\n", + strerror(purb->status), purb->status); +/*---------------------------------------------------------------------------*/ + } else { + for (i = 0; i < purb->number_of_packets; i++) { + if (0 != purb->iso_frame_desc[i].status) { + (peasycap->field_buffer + [peasycap->field_fill][0].kount) |= 0x8000 ; + /* FIXME: 1. missing '-' check boundaries */ + strcpy(&errbuf[0], + strerror(purb->iso_frame_desc[i].status)); + } + framestatus = purb->iso_frame_desc[i].status; + framelength = purb->iso_frame_desc[i].length; + frameactual = purb->iso_frame_desc[i].actual_length; + frameoffset = purb->iso_frame_desc[i].offset; + + JOM(16, "frame[%2i]:" + "%4i=status " + "%4i=actual " + "%4i=length " + "%5i=offset\n", + i, framestatus, frameactual, framelength, frameoffset); + if (!purb->iso_frame_desc[i].status) { + more = purb->iso_frame_desc[i].actual_length; + pfield_buffer = &peasycap->field_buffer + [peasycap->field_fill][peasycap->field_page]; + videofieldamount = (peasycap->field_page * + PAGE_SIZE) + + (int)(pfield_buffer->pto - pfield_buffer->pgo); + if (4 == more) + peasycap->video_mt++; + if (4 < more) { + if (peasycap->video_mt) { + JOM(8, "%4i empty video urb frames\n", + peasycap->video_mt); + peasycap->video_mt = 0; + } + if (FIELD_BUFFER_MANY <= peasycap->field_fill) { + SAM("ERROR: bad peasycap->field_fill\n"); + return; + } + if (FIELD_BUFFER_SIZE/PAGE_SIZE <= + peasycap->field_page) { + SAM("ERROR: bad peasycap->field_page\n"); + return; + } + pfield_buffer = &peasycap->field_buffer + [peasycap->field_fill][peasycap->field_page]; + pu = (u8 *)(purb->transfer_buffer + + purb->iso_frame_desc[i].offset); + if (0x80 & *pu) + leap = 8; + else + leap = 4; +/*--------------------------------------------------------------------------*/ +/* + * EIGHT-BYTE END-OF-VIDEOFIELD MARKER. + * NOTE: A SUCCESSION OF URB FRAMES FOLLOWING THIS ARE EMPTY, + * CORRESPONDING TO THE FIELD FLYBACK (VERTICAL BLANKING) PERIOD. + * + * PROVIDED THE FIELD BUFFER CONTAINS GOOD DATA AS INDICATED BY A ZERO UPPER + * BYTE OF + * peasycap->field_buffer[peasycap->field_fill][0].kount + * THE CONTENTS OF THE FIELD BUFFER ARE OFFERED TO dqbuf(), field_read IS + * UPDATED AND field_fill IS BUMPED. IF THE FIELD BUFFER CONTAINS BAD DATA + * NOTHING IS OFFERED TO dqbuf(). + * + * THE DECISION ON WHETHER THE PARITY OF THE OFFERED FIELD BUFFER IS RIGHT + * RESTS WITH dqbuf(). + */ +/*---------------------------------------------------------------------------*/ + if ((8 == more) || override) { + if (videofieldamount > + peasycap->videofieldamount) { + if (2 == videofieldamount - + peasycap-> + videofieldamount) { + (peasycap->field_buffer + [peasycap->field_fill] + [0].kount) |= 0x0100; + peasycap->video_junk += (1 + + VIDEO_JUNK_TOLERATE); + } else + (peasycap->field_buffer + [peasycap->field_fill] + [0].kount) |= 0x4000; + } else if (videofieldamount < + peasycap-> + videofieldamount) { + (peasycap->field_buffer + [peasycap->field_fill] + [0].kount) |= 0x2000; + } + bad = 0xFF00 & peasycap->field_buffer + [peasycap->field_fill] + [0].kount; + if (!bad) { + (peasycap->video_junk)--; + if (-VIDEO_JUNK_TOLERATE > + peasycap->video_junk) + peasycap->video_junk = + -VIDEO_JUNK_TOLERATE; + peasycap->field_read = + (peasycap-> + field_fill)++; + if (FIELD_BUFFER_MANY <= + peasycap-> + field_fill) + peasycap-> + field_fill = 0; + peasycap->field_page = 0; + pfield_buffer = &peasycap-> + field_buffer + [peasycap-> + field_fill] + [peasycap-> + field_page]; + pfield_buffer->pto = + pfield_buffer->pgo; + JOM(8, "bumped to: %i=" + "peasycap->" + "field_fill %i=" + "parity\n", + peasycap->field_fill, + 0x00FF & + pfield_buffer->kount); + JOM(8, "field buffer %i has " + "%i bytes fit to be " + "read\n", + peasycap->field_read, + videofieldamount); + JOM(8, "wakeup call to " + "wq_video, " + "%i=field_read " + "%i=field_fill " + "%i=parity\n", + peasycap->field_read, + peasycap->field_fill, + 0x00FF & peasycap-> + field_buffer + [peasycap-> + field_read][0].kount); + wake_up_interruptible + (&(peasycap-> + wq_video)); + } else { + peasycap->video_junk++; + if (bad & 0x0010) + peasycap->video_junk += + (1 + VIDEO_JUNK_TOLERATE/2); + JOM(8, "field buffer %i had %i " + "bytes, now discarded: " + "0x%04X\n", + peasycap->field_fill, + videofieldamount, + (0xFF00 & + peasycap->field_buffer + [peasycap->field_fill][0]. + kount)); + (peasycap->field_fill)++; + + if (FIELD_BUFFER_MANY <= + peasycap->field_fill) + peasycap->field_fill = 0; + peasycap->field_page = 0; + pfield_buffer = + &peasycap->field_buffer + [peasycap->field_fill] + [peasycap->field_page]; + pfield_buffer->pto = + pfield_buffer->pgo; + + JOM(8, "bumped to: %i=peasycap->" + "field_fill %i=parity\n", + peasycap->field_fill, + 0x00FF & pfield_buffer->kount); + } + if (8 == more) { + JOM(8, "end-of-field: received " + "parity byte 0x%02X\n", + (0xFF & *pu)); + if (0x40 & *pu) + pfield_buffer->kount = 0x0000; + else + pfield_buffer->kount = 0x0001; + pfield_buffer->input = 0x08 | + (0x07 & peasycap->input); + JOM(8, "end-of-field: 0x%02X=kount\n", + 0xFF & pfield_buffer->kount); + } + } +/*---------------------------------------------------------------------------*/ +/* + * COPY more BYTES FROM ISOC BUFFER TO FIELD BUFFER + */ +/*---------------------------------------------------------------------------*/ + pu += leap; + more -= leap; + + if (FIELD_BUFFER_MANY <= peasycap->field_fill) { + SAM("ERROR: bad peasycap->field_fill\n"); + return; + } + if (FIELD_BUFFER_SIZE/PAGE_SIZE <= peasycap->field_page) { + SAM("ERROR: bad peasycap->field_page\n"); + return; + } + pfield_buffer = &peasycap->field_buffer + [peasycap->field_fill][peasycap->field_page]; + while (more) { + pfield_buffer = &peasycap->field_buffer + [peasycap->field_fill] + [peasycap->field_page]; + if (PAGE_SIZE < (pfield_buffer->pto - + pfield_buffer->pgo)) { + SAM("ERROR: bad pfield_buffer->pto\n"); + return; + } + if (PAGE_SIZE == (pfield_buffer->pto - + pfield_buffer->pgo)) { + (peasycap->field_page)++; + if (FIELD_BUFFER_SIZE/PAGE_SIZE <= + peasycap->field_page) { + JOM(16, "wrapping peasycap->" + "field_page\n"); + peasycap->field_page = 0; + } + pfield_buffer = &peasycap-> + field_buffer + [peasycap->field_fill] + [peasycap->field_page]; + pfield_buffer->pto = pfield_buffer->pgo; + pfield_buffer->input = 0x08 | + (0x07 & peasycap->input); + if ((peasycap->field_buffer[peasycap-> + field_fill][0]). + input != + pfield_buffer->input) + (peasycap->field_buffer + [peasycap->field_fill] + [0]).kount |= 0x1000; + } + + much = PAGE_SIZE - + (int)(pfield_buffer->pto - + pfield_buffer->pgo); + + if (much > more) + much = more; + memcpy(pfield_buffer->pto, pu, much); + pu += much; + (pfield_buffer->pto) += much; + more -= much; + } + } + } + } + } +/*---------------------------------------------------------------------------*/ +/* + * RESUBMIT THIS URB, UNLESS A SEVERE PERSISTENT ERROR CONDITION EXISTS. + * + * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO AN ERROR CONDITION + * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE. + */ +/*---------------------------------------------------------------------------*/ + if (VIDEO_ISOC_BUFFER_MANY <= peasycap->video_junk) { + SAM("easycap driver shutting down on condition green\n"); + peasycap->status = 1; + peasycap->video_eof = 1; + peasycap->video_junk = 0; + wake_up_interruptible(&peasycap->wq_video); +#if !defined(PERSEVERE) + peasycap->audio_eof = 1; + wake_up_interruptible(&peasycap->wq_audio); +#endif /*PERSEVERE*/ + return; + } + if (peasycap->video_isoc_streaming) { + rc = usb_submit_urb(purb, GFP_ATOMIC); + if (rc) { + SAM("%s: %d\n", strerror(rc), rc); + if (-ENODEV != rc) + SAM("ERROR: while %i=video_idle, " + "usb_submit_urb() " + "failed with rc:\n", + peasycap->video_idle); + } + } + return; +} +static const struct file_operations easycap_fops = { + .owner = THIS_MODULE, + .open = easycap_open, + .unlocked_ioctl = easycap_unlocked_ioctl, + .poll = easycap_poll, + .mmap = easycap_mmap, + .llseek = no_llseek, +}; +static const struct usb_class_driver easycap_class = { + .name = "usb/easycap%d", + .fops = &easycap_fops, + .minor_base = USB_SKEL_MINOR_BASE, +}; +/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ +static const struct v4l2_file_operations v4l2_fops = { + .owner = THIS_MODULE, + .open = easycap_open_noinode, + .unlocked_ioctl = easycap_unlocked_ioctl, + .poll = easycap_poll, + .mmap = easycap_mmap, +}; +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE + * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE. + */ +/*---------------------------------------------------------------------------*/ +static int easycap_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct usb_host_interface *alt; + struct usb_endpoint_descriptor *ep; + struct usb_interface_descriptor *interface; + struct urb *purb; + struct easycap *peasycap; + int ndong; + struct data_urb *pdata_urb; + int i, j, k, m, rc; + u8 bInterfaceNumber; + u8 bInterfaceClass; + u8 bInterfaceSubClass; + void *pbuf; + int okalt[8], isokalt; + int okepn[8]; + int okmps[8]; + int maxpacketsize; + u16 mask; + s32 value; + struct easycap_format *peasycap_format; + int fmtidx; + struct inputset *inputset; + + usbdev = interface_to_usbdev(intf); + +/*---------------------------------------------------------------------------*/ + alt = usb_altnum_to_altsetting(intf, 0); + if (!alt) { + SAY("ERROR: usb_host_interface not found\n"); + return -EFAULT; + } + interface = &alt->desc; + if (!interface) { + SAY("ERROR: intf_descriptor is NULL\n"); + return -EFAULT; + } +/*---------------------------------------------------------------------------*/ +/* + * GET PROPERTIES OF PROBED INTERFACE + */ +/*---------------------------------------------------------------------------*/ + bInterfaceNumber = interface->bInterfaceNumber; + bInterfaceClass = interface->bInterfaceClass; + bInterfaceSubClass = interface->bInterfaceSubClass; + + JOT(4, "intf[%i]: num_altsetting=%i\n", + bInterfaceNumber, intf->num_altsetting); + JOT(4, "intf[%i]: cur_altsetting - altsetting=%li\n", + bInterfaceNumber, + (long int)(intf->cur_altsetting - intf->altsetting)); + JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n", + bInterfaceNumber, bInterfaceClass, bInterfaceSubClass); +/*---------------------------------------------------------------------------*/ +/* + * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED. + * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS + * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS + * PHYSICALLY UNPLUGGED. + * + * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN + * INTERFACES 1 AND 2 ARE PROBED. +*/ +/*---------------------------------------------------------------------------*/ + if (0 == bInterfaceNumber) { + peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL); + if (!peasycap) { + SAY("ERROR: Could not allocate peasycap\n"); + return -ENOMEM; + } +/*---------------------------------------------------------------------------*/ +/* + * PERFORM URGENT INTIALIZATIONS ... +*/ +/*---------------------------------------------------------------------------*/ + peasycap->minor = -1; + kref_init(&peasycap->kref); + JOM(8, "intf[%i]: after kref_init(..._video) " + "%i=peasycap->kref.refcount.counter\n", + bInterfaceNumber, peasycap->kref.refcount.counter); + + /* module params */ + peasycap->gain = (s8)clamp(easycap_gain, 0, 31); + + init_waitqueue_head(&peasycap->wq_video); + init_waitqueue_head(&peasycap->wq_audio); + init_waitqueue_head(&peasycap->wq_trigger); + + if (mutex_lock_interruptible(&mutex_dongle)) { + SAY("ERROR: cannot down mutex_dongle\n"); + return -ERESTARTSYS; + } else { +/*---------------------------------------------------------------------------*/ + /* + * FOR INTERFACES 1 AND 2 THE POINTER peasycap WILL NEED TO + * TO BE THE SAME AS THAT ALLOCATED NOW FOR INTERFACE 0. + * + * NORMALLY ndong WILL NOT HAVE CHANGED SINCE INTERFACE 0 WAS + * PROBED, BUT THIS MAY NOT BE THE CASE IF, FOR EXAMPLE, TWO + * EASYCAPs ARE PLUGGED IN SIMULTANEOUSLY. + */ +/*---------------------------------------------------------------------------*/ + for (ndong = 0; ndong < DONGLE_MANY; ndong++) { + if ((!easycapdc60_dongle[ndong].peasycap) && + (!mutex_is_locked(&easycapdc60_dongle + [ndong].mutex_video)) && + (!mutex_is_locked(&easycapdc60_dongle + [ndong].mutex_audio))) { + easycapdc60_dongle[ndong].peasycap = peasycap; + peasycap->isdongle = ndong; + JOM(8, "intf[%i]: peasycap-->easycap" + "_dongle[%i].peasycap\n", + bInterfaceNumber, ndong); + break; + } + } + if (DONGLE_MANY <= ndong) { + SAM("ERROR: too many dongles\n"); + mutex_unlock(&mutex_dongle); + return -ENOMEM; + } + mutex_unlock(&mutex_dongle); + } + peasycap->allocation_video_struct = sizeof(struct easycap); + peasycap->allocation_video_page = 0; + peasycap->allocation_video_urb = 0; + peasycap->allocation_audio_struct = 0; + peasycap->allocation_audio_page = 0; + peasycap->allocation_audio_urb = 0; + +/*---------------------------------------------------------------------------*/ +/* + * ... AND FURTHER INITIALIZE THE STRUCTURE +*/ +/*---------------------------------------------------------------------------*/ + peasycap->pusb_device = usbdev; + peasycap->pusb_interface = intf; + + peasycap->ilk = 0; + peasycap->microphone = false; + + peasycap->video_interface = -1; + peasycap->video_altsetting_on = -1; + peasycap->video_altsetting_off = -1; + peasycap->video_endpointnumber = -1; + peasycap->video_isoc_maxframesize = -1; + peasycap->video_isoc_buffer_size = -1; + + peasycap->audio_interface = -1; + peasycap->audio_altsetting_on = -1; + peasycap->audio_altsetting_off = -1; + peasycap->audio_endpointnumber = -1; + peasycap->audio_isoc_maxframesize = -1; + peasycap->audio_isoc_buffer_size = -1; + + peasycap->frame_buffer_many = FRAME_BUFFER_MANY; + + for (k = 0; k < INPUT_MANY; k++) + peasycap->lost[k] = 0; + peasycap->skip = 0; + peasycap->skipped = 0; + peasycap->offerfields = 0; +/*---------------------------------------------------------------------------*/ +/* + * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ... + */ +/*---------------------------------------------------------------------------*/ + rc = fillin_formats(); + if (0 > rc) { + SAM("ERROR: fillin_formats() rc = %i\n", rc); + return -EFAULT; + } + JOM(4, "%i formats available\n", rc); +/*---------------------------------------------------------------------------*/ +/* + * ... AND POPULATE easycap.inputset[] +*/ +/*---------------------------------------------------------------------------*/ + /* FIXME: maybe we just use memset 0 */ + inputset = peasycap->inputset; + for (k = 0; k < INPUT_MANY; k++) { + inputset[k].input_ok = 0; + inputset[k].standard_offset_ok = 0; + inputset[k].format_offset_ok = 0; + inputset[k].brightness_ok = 0; + inputset[k].contrast_ok = 0; + inputset[k].saturation_ok = 0; + inputset[k].hue_ok = 0; + } + + fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN; + m = 0; + mask = 0; + for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) { + if (fmtidx == easycap_standard[i].v4l2_standard.index) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].standard_offset = i; + + mask = easycap_standard[i].mask; + } + } + + if (1 != m) { + SAM("ERROR: " + "inputset->standard_offset unpopulated, %i=m\n", m); + return -ENOENT; + } + + peasycap_format = &easycap_format[0]; + m = 0; + for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) { + struct v4l2_pix_format *pix = + &peasycap_format->v4l2_format.fmt.pix; + if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) && + pix->field == V4L2_FIELD_NONE && + pix->pixelformat == V4L2_PIX_FMT_UYVY && + pix->width == 640 && pix->height == 480) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].format_offset = i; + break; + } + peasycap_format++; + } + if (1 != m) { + SAM("ERROR: inputset[]->format_offset unpopulated\n"); + return -ENOENT; + } + + m = 0; + for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) { + value = easycap_control[i].default_value; + if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].brightness = value; + } else if (V4L2_CID_CONTRAST == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].contrast = value; + } else if (V4L2_CID_SATURATION == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].saturation = value; + } else if (V4L2_CID_HUE == easycap_control[i].id) { + m++; + for (k = 0; k < INPUT_MANY; k++) + inputset[k].hue = value; + } + } + + if (4 != m) { + SAM("ERROR: inputset[]->brightness underpopulated\n"); + return -ENOENT; + } + for (k = 0; k < INPUT_MANY; k++) + inputset[k].input = k; + JOM(4, "populated inputset[]\n"); + JOM(4, "finished initialization\n"); + } else { +/*---------------------------------------------------------------------------*/ +/* + * FIXME + * + * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2. + * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE. + */ +/*---------------------------------------------------------------------------*/ + for (ndong = 0; ndong < DONGLE_MANY; ndong++) { + if (usbdev == easycapdc60_dongle[ndong].peasycap-> + pusb_device) { + peasycap = easycapdc60_dongle[ndong].peasycap; + JOT(8, "intf[%i]: dongle[%i].peasycap\n", + bInterfaceNumber, ndong); + break; + } + } + if (DONGLE_MANY <= ndong) { + SAY("ERROR: peasycap is unknown when probing interface %i\n", + bInterfaceNumber); + return -ENODEV; + } + if (!peasycap) { + SAY("ERROR: peasycap is NULL when probing interface %i\n", + bInterfaceNumber); + return -ENODEV; + } + } +/*---------------------------------------------------------------------------*/ + if ((USB_CLASS_VIDEO == bInterfaceClass) || + (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) { + if (-1 == peasycap->video_interface) { + peasycap->video_interface = bInterfaceNumber; + JOM(4, "setting peasycap->video_interface=%i\n", + peasycap->video_interface); + } else { + if (peasycap->video_interface != bInterfaceNumber) { + SAM("ERROR: attempting to reset " + "peasycap->video_interface\n"); + SAM("...... continuing with " + "%i=peasycap->video_interface\n", + peasycap->video_interface); + } + } + } else if ((USB_CLASS_AUDIO == bInterfaceClass) && + (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) { + if (-1 == peasycap->audio_interface) { + peasycap->audio_interface = bInterfaceNumber; + JOM(4, "setting peasycap->audio_interface=%i\n", + peasycap->audio_interface); + } else { + if (peasycap->audio_interface != bInterfaceNumber) { + SAM("ERROR: attempting to reset " + "peasycap->audio_interface\n"); + SAM("...... continuing with " + "%i=peasycap->audio_interface\n", + peasycap->audio_interface); + } + } + } +/*---------------------------------------------------------------------------*/ +/* + * INVESTIGATE ALL ALTSETTINGS. + * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS. + */ +/*---------------------------------------------------------------------------*/ + isokalt = 0; + + for (i = 0; i < intf->num_altsetting; i++) { + alt = usb_altnum_to_altsetting(intf, i); + if (!alt) { + SAM("ERROR: alt is NULL\n"); + return -EFAULT; + } + interface = &alt->desc; + if (!interface) { + SAM("ERROR: intf_descriptor is NULL\n"); + return -EFAULT; + } + + if (0 == interface->bNumEndpoints) + JOM(4, "intf[%i]alt[%i] has no endpoints\n", + bInterfaceNumber, i); +/*---------------------------------------------------------------------------*/ + for (j = 0; j < interface->bNumEndpoints; j++) { + ep = &alt->endpoint[j].desc; + if (!ep) { + SAM("ERROR: ep is NULL.\n"); + SAM("...... skipping\n"); + continue; + } + + if (!usb_endpoint_is_isoc_in(ep)) { + JOM(4, "intf[%i]alt[%i]end[%i] is a %d endpoint\n", + bInterfaceNumber, + i, j, ep->bmAttributes); + if (usb_endpoint_dir_out(ep)) { + SAM("ERROR: OUT endpoint unexpected\n"); + SAM("...... continuing\n"); + } + continue; + } + switch (bInterfaceClass) { + case USB_CLASS_VIDEO: + case USB_CLASS_VENDOR_SPEC: { + if (ep->wMaxPacketSize) { + if (8 > isokalt) { + okalt[isokalt] = i; + JOM(4, + "%i=okalt[%i]\n", + okalt[isokalt], + isokalt); + okepn[isokalt] = + ep-> + bEndpointAddress & + 0x0F; + JOM(4, + "%i=okepn[%i]\n", + okepn[isokalt], + isokalt); + okmps[isokalt] = + le16_to_cpu(ep-> + wMaxPacketSize); + JOM(4, + "%i=okmps[%i]\n", + okmps[isokalt], + isokalt); + isokalt++; + } + } else { + if (-1 == peasycap-> + video_altsetting_off) { + peasycap-> + video_altsetting_off = + i; + JOM(4, "%i=video_" + "altsetting_off " + "<====\n", + peasycap-> + video_altsetting_off); + } else { + SAM("ERROR: peasycap" + "->video_altsetting_" + "off already set\n"); + SAM("...... " + "continuing with " + "%i=peasycap->video_" + "altsetting_off\n", + peasycap-> + video_altsetting_off); + } + } + break; + } + case USB_CLASS_AUDIO: { + if (bInterfaceSubClass != + USB_SUBCLASS_AUDIOSTREAMING) + break; + if (!peasycap) { + SAM("MISTAKE: " + "peasycap is NULL\n"); + return -EFAULT; + } + if (ep->wMaxPacketSize) { + if (8 > isokalt) { + okalt[isokalt] = i ; + JOM(4, + "%i=okalt[%i]\n", + okalt[isokalt], + isokalt); + okepn[isokalt] = + ep-> + bEndpointAddress & + 0x0F; + JOM(4, + "%i=okepn[%i]\n", + okepn[isokalt], + isokalt); + okmps[isokalt] = + le16_to_cpu(ep-> + wMaxPacketSize); + JOM(4, + "%i=okmps[%i]\n", + okmps[isokalt], + isokalt); + isokalt++; + } + } else { + if (-1 == peasycap-> + audio_altsetting_off) { + peasycap-> + audio_altsetting_off = + i; + JOM(4, "%i=audio_" + "altsetting_off " + "<====\n", + peasycap-> + audio_altsetting_off); + } else { + SAM("ERROR: peasycap" + "->audio_altsetting_" + "off already set\n"); + SAM("...... " + "continuing with " + "%i=peasycap->" + "audio_altsetting_" + "off\n", + peasycap-> + audio_altsetting_off); + } + } + break; + } + default: + break; + } + if (0 == ep->wMaxPacketSize) { + JOM(4, "intf[%i]alt[%i]end[%i] " + "has zero packet size\n", + bInterfaceNumber, i, j); + } + } + } +/*---------------------------------------------------------------------------*/ +/* + * PERFORM INITIALIZATION OF THE PROBED INTERFACE + */ +/*---------------------------------------------------------------------------*/ + JOM(4, "initialization begins for interface %i\n", + interface->bInterfaceNumber); + switch (bInterfaceNumber) { +/*---------------------------------------------------------------------------*/ +/* + * INTERFACE 0 IS THE VIDEO INTERFACE + */ +/*---------------------------------------------------------------------------*/ + case 0: { + if (!peasycap) { + SAM("MISTAKE: peasycap is NULL\n"); + return -EFAULT; + } + if (!isokalt) { + SAM("ERROR: no viable video_altsetting_on\n"); + return -ENOENT; + } else { + peasycap->video_altsetting_on = okalt[isokalt - 1]; + JOM(4, "%i=video_altsetting_on <====\n", + peasycap->video_altsetting_on); + } +/*---------------------------------------------------------------------------*/ +/* + * DECIDE THE VIDEO STREAMING PARAMETERS + */ +/*---------------------------------------------------------------------------*/ + peasycap->video_endpointnumber = okepn[isokalt - 1]; + JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber); + maxpacketsize = okmps[isokalt - 1]; + + peasycap->video_isoc_maxframesize = + min(maxpacketsize, USB_2_0_MAXPACKETSIZE); + if (0 >= peasycap->video_isoc_maxframesize) { + SAM("ERROR: bad video_isoc_maxframesize\n"); + SAM(" possibly because port is USB 1.1\n"); + return -ENOENT; + } + JOM(4, "%i=video_isoc_maxframesize\n", + peasycap->video_isoc_maxframesize); + + peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC; + JOM(4, "%i=video_isoc_framesperdesc\n", + peasycap->video_isoc_framesperdesc); + if (0 >= peasycap->video_isoc_framesperdesc) { + SAM("ERROR: bad video_isoc_framesperdesc\n"); + return -ENOENT; + } + peasycap->video_isoc_buffer_size = + peasycap->video_isoc_maxframesize * + peasycap->video_isoc_framesperdesc; + JOM(4, "%i=video_isoc_buffer_size\n", + peasycap->video_isoc_buffer_size); + if ((PAGE_SIZE << VIDEO_ISOC_ORDER) < + peasycap->video_isoc_buffer_size) { + SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n"); + return -EFAULT; + } +/*---------------------------------------------------------------------------*/ + if (-1 == peasycap->video_interface) { + SAM("MISTAKE: video_interface is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->video_altsetting_on) { + SAM("MISTAKE: video_altsetting_on is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->video_altsetting_off) { + SAM("MISTAKE: video_interface_off is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->video_endpointnumber) { + SAM("MISTAKE: video_endpointnumber is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->video_isoc_maxframesize) { + SAM("MISTAKE: video_isoc_maxframesize is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->video_isoc_buffer_size) { + SAM("MISTAKE: video_isoc_buffer_size is unset\n"); + return -EFAULT; + } +/*---------------------------------------------------------------------------*/ +/* + * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST. + */ +/*---------------------------------------------------------------------------*/ + INIT_LIST_HEAD(&(peasycap->urb_video_head)); + peasycap->purb_video_head = &(peasycap->urb_video_head); +/*---------------------------------------------------------------------------*/ + JOM(4, "allocating %i frame buffers of size %li\n", + FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE); + JOM(4, ".... each scattered over %li pages\n", + FRAME_BUFFER_SIZE/PAGE_SIZE); + + for (k = 0; k < FRAME_BUFFER_MANY; k++) { + for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->frame_buffer[k][m].pgo) + SAM("attempting to reallocate frame " + " buffers\n"); + else { + pbuf = (void *)__get_free_page(GFP_KERNEL); + if (!pbuf) { + SAM("ERROR: Could not allocate frame " + "buffer %i page %i\n", k, m); + return -ENOMEM; + } else + peasycap->allocation_video_page += 1; + peasycap->frame_buffer[k][m].pgo = pbuf; + } + peasycap->frame_buffer[k][m].pto = + peasycap->frame_buffer[k][m].pgo; + } + } + + peasycap->frame_fill = 0; + peasycap->frame_read = 0; + JOM(4, "allocation of frame buffers done: %i pages\n", k * + m); +/*---------------------------------------------------------------------------*/ + JOM(4, "allocating %i field buffers of size %li\n", + FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE); + JOM(4, ".... each scattered over %li pages\n", + FIELD_BUFFER_SIZE/PAGE_SIZE); + + for (k = 0; k < FIELD_BUFFER_MANY; k++) { + for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) { + if (peasycap->field_buffer[k][m].pgo) { + SAM("ERROR: attempting to reallocate " + "field buffers\n"); + } else { + pbuf = (void *) __get_free_page(GFP_KERNEL); + if (!pbuf) { + SAM("ERROR: Could not allocate field" + " buffer %i page %i\n", k, m); + return -ENOMEM; + } + else + peasycap->allocation_video_page += 1; + peasycap->field_buffer[k][m].pgo = pbuf; + } + peasycap->field_buffer[k][m].pto = + peasycap->field_buffer[k][m].pgo; + } + peasycap->field_buffer[k][0].kount = 0x0200; + } + peasycap->field_fill = 0; + peasycap->field_page = 0; + peasycap->field_read = 0; + JOM(4, "allocation of field buffers done: %i pages\n", k * + m); +/*---------------------------------------------------------------------------*/ + JOM(4, "allocating %i isoc video buffers of size %i\n", + VIDEO_ISOC_BUFFER_MANY, + peasycap->video_isoc_buffer_size); + JOM(4, ".... each occupying contiguous memory pages\n"); + + for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { + pbuf = (void *)__get_free_pages(GFP_KERNEL, + VIDEO_ISOC_ORDER); + if (!pbuf) { + SAM("ERROR: Could not allocate isoc video buffer " + "%i\n", k); + return -ENOMEM; + } else + peasycap->allocation_video_page += + BIT(VIDEO_ISOC_ORDER); + + peasycap->video_isoc_buffer[k].pgo = pbuf; + peasycap->video_isoc_buffer[k].pto = + pbuf + peasycap->video_isoc_buffer_size; + peasycap->video_isoc_buffer[k].kount = k; + } + JOM(4, "allocation of isoc video buffers done: %i pages\n", + k * (0x01 << VIDEO_ISOC_ORDER)); +/*---------------------------------------------------------------------------*/ +/* + * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... + */ +/*---------------------------------------------------------------------------*/ + JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY); + JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n", + peasycap->video_isoc_framesperdesc); + JOM(4, "using %i=peasycap->video_isoc_maxframesize\n", + peasycap->video_isoc_maxframesize); + JOM(4, "using %i=peasycap->video_isoc_buffer_sizen", + peasycap->video_isoc_buffer_size); + + for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) { + purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc, + GFP_KERNEL); + if (!purb) { + SAM("ERROR: usb_alloc_urb returned NULL for buffer " + "%i\n", k); + return -ENOMEM; + } else + peasycap->allocation_video_urb += 1; +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); + if (!pdata_urb) { + SAM("ERROR: Could not allocate struct data_urb.\n"); + return -ENOMEM; + } else + peasycap->allocation_video_struct += + sizeof(struct data_urb); + + pdata_urb->purb = purb; + pdata_urb->isbuf = k; + pdata_urb->length = 0; + list_add_tail(&(pdata_urb->list_head), + peasycap->purb_video_head); +/*---------------------------------------------------------------------------*/ +/* + * ... AND INITIALIZE THEM + */ +/*---------------------------------------------------------------------------*/ + if (!k) { + JOM(4, "initializing video urbs thus:\n"); + JOM(4, " purb->interval = 1;\n"); + JOM(4, " purb->dev = peasycap->pusb_device;\n"); + JOM(4, " purb->pipe = usb_rcvisocpipe" + "(peasycap->pusb_device,%i);\n", + peasycap->video_endpointnumber); + JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); + JOM(4, " purb->transfer_buffer = peasycap->" + "video_isoc_buffer[.].pgo;\n"); + JOM(4, " purb->transfer_buffer_length = %i;\n", + peasycap->video_isoc_buffer_size); + JOM(4, " purb->complete = easycap_complete;\n"); + JOM(4, " purb->context = peasycap;\n"); + JOM(4, " purb->start_frame = 0;\n"); + JOM(4, " purb->number_of_packets = %i;\n", + peasycap->video_isoc_framesperdesc); + JOM(4, " for (j = 0; j < %i; j++)\n", + peasycap->video_isoc_framesperdesc); + JOM(4, " {\n"); + JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", + peasycap->video_isoc_maxframesize); + JOM(4, " purb->iso_frame_desc[j].length = %i;\n", + peasycap->video_isoc_maxframesize); + JOM(4, " }\n"); + } + + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, + peasycap->video_endpointnumber); + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo; + purb->transfer_buffer_length = + peasycap->video_isoc_buffer_size; + purb->complete = easycap_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = peasycap->video_isoc_framesperdesc; + for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = j * + peasycap->video_isoc_maxframesize; + purb->iso_frame_desc[j].length = + peasycap->video_isoc_maxframesize; + } + } + JOM(4, "allocation of %i struct urb done.\n", k); +/*--------------------------------------------------------------------------*/ +/* + * SAVE POINTER peasycap IN THIS INTERFACE. + */ +/*--------------------------------------------------------------------------*/ + usb_set_intfdata(intf, peasycap); +/*---------------------------------------------------------------------------*/ +/* + * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER, + * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE + * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH. + * BEWARE. +*/ +/*---------------------------------------------------------------------------*/ + peasycap->ntsc = easycap_ntsc; + JOM(8, "defaulting initially to %s\n", + easycap_ntsc ? "NTSC" : "PAL"); + rc = reset(peasycap); + if (rc) { + SAM("ERROR: reset() rc = %i\n", rc); + return -EFAULT; + } +/*--------------------------------------------------------------------------*/ +/* + * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. + */ +/*--------------------------------------------------------------------------*/ + if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) { + SAM("v4l2_device_register() failed\n"); + return -ENODEV; + } + JOM(4, "registered device instance: %s\n", + peasycap->v4l2_device.name); +/*---------------------------------------------------------------------------*/ +/* + * FIXME + * + * + * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG: +*/ +/*---------------------------------------------------------------------------*/ + peasycap->video_device.v4l2_dev = NULL; +/*---------------------------------------------------------------------------*/ + + + strcpy(&peasycap->video_device.name[0], "easycapdc60"); + peasycap->video_device.fops = &v4l2_fops; + peasycap->video_device.minor = -1; + peasycap->video_device.release = (void *)(&videodev_release); + + video_set_drvdata(&(peasycap->video_device), (void *)peasycap); + + if (0 != (video_register_device(&(peasycap->video_device), + VFL_TYPE_GRABBER, -1))) { + err("Not able to register with videodev"); + videodev_release(&(peasycap->video_device)); + return -ENODEV; + } else { + (peasycap->registered_video)++; + SAM("registered with videodev: %i=minor\n", + peasycap->video_device.minor); + peasycap->minor = peasycap->video_device.minor; + } +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + break; + } +/*--------------------------------------------------------------------------*/ +/* + * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE + * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE + */ +/*--------------------------------------------------------------------------*/ + case 1: { + if (!peasycap) { + SAM("MISTAKE: peasycap is NULL\n"); + return -EFAULT; + } +/*--------------------------------------------------------------------------*/ +/* + * SAVE POINTER peasycap IN INTERFACE 1 + */ +/*--------------------------------------------------------------------------*/ + usb_set_intfdata(intf, peasycap); + JOM(4, "no initialization required for interface %i\n", + interface->bInterfaceNumber); + break; + } +/*--------------------------------------------------------------------------*/ + case 2: { + if (!peasycap) { + SAM("MISTAKE: peasycap is NULL\n"); + return -EFAULT; + } + if (!isokalt) { + SAM("ERROR: no viable audio_altsetting_on\n"); + return -ENOENT; + } else { + peasycap->audio_altsetting_on = okalt[isokalt - 1]; + JOM(4, "%i=audio_altsetting_on <====\n", + peasycap->audio_altsetting_on); + } + + peasycap->audio_endpointnumber = okepn[isokalt - 1]; + JOM(4, "%i=audio_endpointnumber\n", peasycap->audio_endpointnumber); + + peasycap->audio_isoc_maxframesize = okmps[isokalt - 1]; + JOM(4, "%i=audio_isoc_maxframesize\n", + peasycap->audio_isoc_maxframesize); + if (0 >= peasycap->audio_isoc_maxframesize) { + SAM("ERROR: bad audio_isoc_maxframesize\n"); + return -ENOENT; + } + if (9 == peasycap->audio_isoc_maxframesize) { + peasycap->ilk |= 0x02; + SAM("audio hardware is microphone\n"); + peasycap->microphone = true; + peasycap->audio_pages_per_fragment = + PAGES_PER_AUDIO_FRAGMENT; + } else if (256 == peasycap->audio_isoc_maxframesize) { + peasycap->ilk &= ~0x02; + SAM("audio hardware is AC'97\n"); + peasycap->microphone = false; + peasycap->audio_pages_per_fragment = + PAGES_PER_AUDIO_FRAGMENT; + } else { + SAM("hardware is unidentified:\n"); + SAM("%i=audio_isoc_maxframesize\n", + peasycap->audio_isoc_maxframesize); + return -ENOENT; + } + + peasycap->audio_bytes_per_fragment = + peasycap->audio_pages_per_fragment * PAGE_SIZE; + peasycap->audio_buffer_page_many = (AUDIO_FRAGMENT_MANY * + peasycap->audio_pages_per_fragment); + + JOM(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY); + JOM(4, "%6i=audio_pages_per_fragment\n", + peasycap->audio_pages_per_fragment); + JOM(4, "%6i=audio_bytes_per_fragment\n", + peasycap->audio_bytes_per_fragment); + JOM(4, "%6i=audio_buffer_page_many\n", + peasycap->audio_buffer_page_many); + + peasycap->audio_isoc_framesperdesc = AUDIO_ISOC_FRAMESPERDESC; + + JOM(4, "%i=audio_isoc_framesperdesc\n", + peasycap->audio_isoc_framesperdesc); + if (0 >= peasycap->audio_isoc_framesperdesc) { + SAM("ERROR: bad audio_isoc_framesperdesc\n"); + return -ENOENT; + } + + peasycap->audio_isoc_buffer_size = + peasycap->audio_isoc_maxframesize * + peasycap->audio_isoc_framesperdesc; + JOM(4, "%i=audio_isoc_buffer_size\n", + peasycap->audio_isoc_buffer_size); + if (AUDIO_ISOC_BUFFER_SIZE < peasycap->audio_isoc_buffer_size) { + SAM("MISTAKE: audio_isoc_buffer_size bigger " + "than %li=AUDIO_ISOC_BUFFER_SIZE\n", + AUDIO_ISOC_BUFFER_SIZE); + return -EFAULT; + } + if (-1 == peasycap->audio_interface) { + SAM("MISTAKE: audio_interface is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->audio_altsetting_on) { + SAM("MISTAKE: audio_altsetting_on is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->audio_altsetting_off) { + SAM("MISTAKE: audio_interface_off is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->audio_endpointnumber) { + SAM("MISTAKE: audio_endpointnumber is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->audio_isoc_maxframesize) { + SAM("MISTAKE: audio_isoc_maxframesize is unset\n"); + return -EFAULT; + } + if (-1 == peasycap->audio_isoc_buffer_size) { + SAM("MISTAKE: audio_isoc_buffer_size is unset\n"); + return -EFAULT; + } +/*---------------------------------------------------------------------------*/ +/* + * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST. + */ +/*---------------------------------------------------------------------------*/ + INIT_LIST_HEAD(&(peasycap->urb_audio_head)); + peasycap->purb_audio_head = &(peasycap->urb_audio_head); + +/*---------------------------------------------------------------------------*/ + JOM(4, "allocating %i isoc audio buffers of size %i\n", + AUDIO_ISOC_BUFFER_MANY, + peasycap->audio_isoc_buffer_size); + JOM(4, ".... each occupying contiguous memory pages\n"); + + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + pbuf = (void *)__get_free_pages(GFP_KERNEL, + AUDIO_ISOC_ORDER); + if (!pbuf) { + SAM("ERROR: Could not allocate isoc audio buffer " + "%i\n", k); + return -ENOMEM; + } else + peasycap->allocation_audio_page += + BIT(AUDIO_ISOC_ORDER); + + peasycap->audio_isoc_buffer[k].pgo = pbuf; + peasycap->audio_isoc_buffer[k].pto = pbuf + + peasycap->audio_isoc_buffer_size; + peasycap->audio_isoc_buffer[k].kount = k; + } + JOM(4, "allocation of isoc audio buffers done.\n"); +/*---------------------------------------------------------------------------*/ +/* + * ALLOCATE AND INITIALIZE MULTIPLE struct urb ... + */ +/*---------------------------------------------------------------------------*/ + JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY); + JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n", + peasycap->audio_isoc_maxframesize); + JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n", + peasycap->audio_isoc_buffer_size); + + for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) { + purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc, + GFP_KERNEL); + if (!purb) { + SAM("ERROR: usb_alloc_urb returned NULL for buffer " + "%i\n", k); + return -ENOMEM; + } + peasycap->allocation_audio_urb += 1 ; +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL); + if (!pdata_urb) { + SAM("ERROR: Could not allocate struct data_urb.\n"); + return -ENOMEM; + } + peasycap->allocation_audio_struct += + sizeof(struct data_urb); + + pdata_urb->purb = purb; + pdata_urb->isbuf = k; + pdata_urb->length = 0; + list_add_tail(&(pdata_urb->list_head), + peasycap->purb_audio_head); +/*---------------------------------------------------------------------------*/ +/* + * ... AND INITIALIZE THEM + */ +/*---------------------------------------------------------------------------*/ + if (!k) { + JOM(4, "initializing audio urbs thus:\n"); + JOM(4, " purb->interval = 1;\n"); + JOM(4, " purb->dev = peasycap->pusb_device;\n"); + JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->" + "pusb_device,%i);\n", + peasycap->audio_endpointnumber); + JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n"); + JOM(4, " purb->transfer_buffer = " + "peasycap->audio_isoc_buffer[.].pgo;\n"); + JOM(4, " purb->transfer_buffer_length = %i;\n", + peasycap->audio_isoc_buffer_size); + JOM(4, " purb->complete = easycap_alsa_complete;\n"); + JOM(4, " purb->context = peasycap;\n"); + JOM(4, " purb->start_frame = 0;\n"); + JOM(4, " purb->number_of_packets = %i;\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, " for (j = 0; j < %i; j++)\n", + peasycap->audio_isoc_framesperdesc); + JOM(4, " {\n"); + JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n", + peasycap->audio_isoc_maxframesize); + JOM(4, " purb->iso_frame_desc[j].length = %i;\n", + peasycap->audio_isoc_maxframesize); + JOM(4, " }\n"); + } + + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, + peasycap->audio_endpointnumber); + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo; + purb->transfer_buffer_length = + peasycap->audio_isoc_buffer_size; + purb->complete = easycap_alsa_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = peasycap->audio_isoc_framesperdesc; + for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = j * + peasycap->audio_isoc_maxframesize; + purb->iso_frame_desc[j].length = + peasycap->audio_isoc_maxframesize; + } + } + JOM(4, "allocation of %i struct urb done.\n", k); +/*---------------------------------------------------------------------------*/ +/* + * SAVE POINTER peasycap IN THIS INTERFACE. + */ +/*---------------------------------------------------------------------------*/ + usb_set_intfdata(intf, peasycap); +/*---------------------------------------------------------------------------*/ +/* + * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY. + */ +/*---------------------------------------------------------------------------*/ + JOM(4, "initializing ALSA card\n"); + + rc = easycap_alsa_probe(peasycap); + if (rc) { + err("easycap_alsa_probe() rc = %i\n", rc); + return -ENODEV; + } + + + JOM(8, "kref_get() with %i=kref.refcount.counter\n", + peasycap->kref.refcount.counter); + kref_get(&peasycap->kref); + peasycap->registered_audio++; + break; + } +/*---------------------------------------------------------------------------*/ +/* + * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED + */ +/*---------------------------------------------------------------------------*/ + default: + JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber); + return -EINVAL; + } + SAM("ends successfully for interface %i\n", bInterfaceNumber); + return 0; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY + * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID. + * + * THIS FUNCTION AFFECTS ALSA. BEWARE. + */ +/*---------------------------------------------------------------------------*/ +static void easycap_usb_disconnect(struct usb_interface *pusb_interface) +{ + struct usb_host_interface *pusb_host_interface; + struct usb_interface_descriptor *pusb_interface_descriptor; + u8 bInterfaceNumber; + struct easycap *peasycap; + + struct list_head *plist_head; + struct data_urb *pdata_urb; + int minor, m, kd; + + JOT(4, "\n"); + + pusb_host_interface = pusb_interface->cur_altsetting; + if (!pusb_host_interface) { + JOT(4, "ERROR: pusb_host_interface is NULL\n"); + return; + } + pusb_interface_descriptor = &(pusb_host_interface->desc); + if (!pusb_interface_descriptor) { + JOT(4, "ERROR: pusb_interface_descriptor is NULL\n"); + return; + } + bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber; + minor = pusb_interface->minor; + JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor); + + if (1 == bInterfaceNumber) + return; + + peasycap = usb_get_intfdata(pusb_interface); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return; + } +/*---------------------------------------------------------------------------*/ +/* + * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE. +*/ +/*---------------------------------------------------------------------------*/ + peasycap->video_eof = 1; + peasycap->audio_eof = 1; + wake_up_interruptible(&(peasycap->wq_video)); + wake_up_interruptible(&(peasycap->wq_audio)); +/*---------------------------------------------------------------------------*/ + switch (bInterfaceNumber) { + case 0: { + if (peasycap->purb_video_head) { + JOM(4, "killing video urbs\n"); + m = 0; + list_for_each(plist_head, peasycap->purb_video_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + if (pdata_urb->purb) { + usb_kill_urb(pdata_urb->purb); + m++; + } + } + } + JOM(4, "%i video urbs killed\n", m); + } + break; + } +/*---------------------------------------------------------------------------*/ + case 2: { + if (peasycap->purb_audio_head) { + JOM(4, "killing audio urbs\n"); + m = 0; + list_for_each(plist_head, peasycap->purb_audio_head) { + pdata_urb = list_entry(plist_head, + struct data_urb, list_head); + if (pdata_urb) { + if (pdata_urb->purb) { + usb_kill_urb(pdata_urb->purb); + m++; + } + } + } + JOM(4, "%i audio urbs killed\n", m); + } + break; + } + default: + break; + } +/*--------------------------------------------------------------------------*/ +/* + * DEREGISTER + * + * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO + * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN + * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE. + */ +/*--------------------------------------------------------------------------*/ + kd = isdongle(peasycap); + switch (bInterfaceNumber) { + case 0: { + if (0 <= kd && DONGLE_MANY > kd) { + wake_up_interruptible(&peasycap->wq_video); + JOM(4, "about to lock dongle[%i].mutex_video\n", kd); + if (mutex_lock_interruptible(&easycapdc60_dongle[kd]. + mutex_video)) { + SAY("ERROR: " + "cannot lock dongle[%i].mutex_video\n", kd); + return; + } + JOM(4, "locked dongle[%i].mutex_video\n", kd); + } else { + SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); + } +/*---------------------------------------------------------------------------*/ + if (!peasycap->v4l2_device.name[0]) { + SAM("ERROR: peasycap->v4l2_device.name is empty\n"); + if (0 <= kd && DONGLE_MANY > kd) + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + return; + } + v4l2_device_disconnect(&peasycap->v4l2_device); + JOM(4, "v4l2_device_disconnect() OK\n"); + v4l2_device_unregister(&peasycap->v4l2_device); + JOM(4, "v4l2_device_unregister() OK\n"); + + video_unregister_device(&peasycap->video_device); + JOM(4, "intf[%i]: video_unregister_device() minor=%i\n", + bInterfaceNumber, minor); + peasycap->registered_video--; +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + + if (0 <= kd && DONGLE_MANY > kd) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + JOM(4, "unlocked dongle[%i].mutex_video\n", kd); + } + break; + } + case 2: { + if (0 <= kd && DONGLE_MANY > kd) { + wake_up_interruptible(&peasycap->wq_audio); + JOM(4, "about to lock dongle[%i].mutex_audio\n", kd); + if (mutex_lock_interruptible(&easycapdc60_dongle[kd]. + mutex_audio)) { + SAY("ERROR: " + "cannot lock dongle[%i].mutex_audio\n", kd); + return; + } + JOM(4, "locked dongle[%i].mutex_audio\n", kd); + } else + SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd); + if (0 != snd_card_free(peasycap->psnd_card)) { + SAY("ERROR: snd_card_free() failed\n"); + } else { + peasycap->psnd_card = NULL; + (peasycap->registered_audio)--; + } + if (0 <= kd && DONGLE_MANY > kd) { + mutex_unlock(&easycapdc60_dongle[kd].mutex_audio); + JOM(4, "unlocked dongle[%i].mutex_audio\n", kd); + } + break; + } + default: + break; + } +/*---------------------------------------------------------------------------*/ +/* + * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap + * (ALSO WHEN ALSA HAS BEEN IN USE) + */ +/*---------------------------------------------------------------------------*/ + if (!peasycap->kref.refcount.counter) { + SAM("ERROR: peasycap->kref.refcount.counter is zero " + "so cannot call kref_put()\n"); + SAM("ending unsuccessfully: may cause memory leak\n"); + return; + } + if (0 <= kd && DONGLE_MANY > kd) { + JOM(4, "about to lock dongle[%i].mutex_video\n", kd); + if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) { + SAY("ERROR: cannot lock dongle[%i].mutex_video\n", kd); + SAM("ending unsuccessfully: may cause memory leak\n"); + return; + } + JOM(4, "locked dongle[%i].mutex_video\n", kd); + JOM(4, "about to lock dongle[%i].mutex_audio\n", kd); + if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_audio)) { + SAY("ERROR: cannot lock dongle[%i].mutex_audio\n", kd); + mutex_unlock(&(easycapdc60_dongle[kd].mutex_video)); + JOM(4, "unlocked dongle[%i].mutex_video\n", kd); + SAM("ending unsuccessfully: may cause memory leak\n"); + return; + } + JOM(4, "locked dongle[%i].mutex_audio\n", kd); + } + JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n", + bInterfaceNumber, (int)peasycap->kref.refcount.counter); + kref_put(&peasycap->kref, easycap_delete); + JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber); + if (0 <= kd && DONGLE_MANY > kd) { + mutex_unlock(&(easycapdc60_dongle[kd].mutex_audio)); + JOT(4, "unlocked dongle[%i].mutex_audio\n", kd); + mutex_unlock(&easycapdc60_dongle[kd].mutex_video); + JOT(4, "unlocked dongle[%i].mutex_video\n", kd); + } +/*---------------------------------------------------------------------------*/ + JOM(4, "ends\n"); + return; +} +/*****************************************************************************/ + +/*---------------------------------------------------------------------------*/ +/* + * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO + */ +/*---------------------------------------------------------------------------*/ +static struct usb_device_id easycap_usb_device_id_table[] = { + {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)}, + { } +}; + +MODULE_DEVICE_TABLE(usb, easycap_usb_device_id_table); +struct usb_driver easycap_usb_driver = { + .name = "easycap", + .id_table = easycap_usb_device_id_table, + .probe = easycap_usb_probe, + .disconnect = easycap_usb_disconnect, +}; + +static int __init easycap_module_init(void) +{ + int k, rc; + + printk(KERN_INFO "Easycap version: "EASYCAP_DRIVER_VERSION "\n"); + + JOT(4, "begins. %i=debug %i=bars %i=gain\n", + easycap_debug, easycap_bars, easycap_gain); + + mutex_init(&mutex_dongle); + for (k = 0; k < DONGLE_MANY; k++) { + easycapdc60_dongle[k].peasycap = NULL; + mutex_init(&easycapdc60_dongle[k].mutex_video); + mutex_init(&easycapdc60_dongle[k].mutex_audio); + } + rc = usb_register(&easycap_usb_driver); + if (rc) + printk(KERN_ERR "Easycap: usb_register failed rc=%d\n", rc); + + return rc; +} +/*****************************************************************************/ +static void __exit easycap_module_exit(void) +{ + usb_deregister(&easycap_usb_driver); +} +/*****************************************************************************/ + +module_init(easycap_module_init); +module_exit(easycap_module_exit); + +/*****************************************************************************/ diff --git a/drivers/staging/media/easycap/easycap_settings.c b/drivers/staging/media/easycap/easycap_settings.c new file mode 100644 index 000000000000..70f59b13c34d --- /dev/null +++ b/drivers/staging/media/easycap/easycap_settings.c @@ -0,0 +1,696 @@ +/****************************************************************************** +* * +* easycap_settings.c * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +/*****************************************************************************/ + +#include "easycap.h" + +/*---------------------------------------------------------------------------*/ +/* + * THE LEAST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING: + * 0 => 25 fps + * 1 => 30 fps + * + * THE MOST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING: + * 0 => full framerate + * 1 => 20% framerate + */ +/*---------------------------------------------------------------------------*/ +const struct easycap_standard easycap_standard[] = { + { + .mask = 0x00FF & PAL_BGHIN , + .v4l2_standard = { + .index = PAL_BGHIN, + .id = (V4L2_STD_PAL_B | + V4L2_STD_PAL_G | V4L2_STD_PAL_H | + V4L2_STD_PAL_I | V4L2_STD_PAL_N), + .name = "PAL_BGHIN", + .frameperiod = {1, 25}, + .framelines = 625, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & NTSC_N_443 , + .v4l2_standard = { + .index = NTSC_N_443, + .id = V4L2_STD_UNKNOWN, + .name = "NTSC_N_443", + .frameperiod = {1, 25}, + .framelines = 480, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & PAL_Nc , + .v4l2_standard = { + .index = PAL_Nc, + .id = V4L2_STD_PAL_Nc, + .name = "PAL_Nc", + .frameperiod = {1, 25}, + .framelines = 625, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & NTSC_N , + .v4l2_standard = { + .index = NTSC_N, + .id = V4L2_STD_UNKNOWN, + .name = "NTSC_N", + .frameperiod = {1, 25}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & SECAM , + .v4l2_standard = { + .index = SECAM, + .id = V4L2_STD_SECAM, + .name = "SECAM", + .frameperiod = {1, 25}, + .framelines = 625, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & NTSC_M , + .v4l2_standard = { + .index = NTSC_M, + .id = V4L2_STD_NTSC_M, + .name = "NTSC_M", + .frameperiod = {1, 30}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & NTSC_M_JP , + .v4l2_standard = { + .index = NTSC_M_JP, + .id = V4L2_STD_NTSC_M_JP, + .name = "NTSC_M_JP", + .frameperiod = {1, 30}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & PAL_60 , + .v4l2_standard = { + .index = PAL_60, + .id = V4L2_STD_PAL_60, + .name = "PAL_60", + .frameperiod = {1, 30}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & NTSC_443 , + .v4l2_standard = { + .index = NTSC_443, + .id = V4L2_STD_NTSC_443, + .name = "NTSC_443", + .frameperiod = {1, 30}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x00FF & PAL_M , + .v4l2_standard = { + .index = PAL_M, + .id = V4L2_STD_PAL_M, + .name = "PAL_M", + .frameperiod = {1, 30}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & PAL_BGHIN_SLOW), + .v4l2_standard = { + .index = PAL_BGHIN_SLOW, + .id = (V4L2_STD_PAL_B | V4L2_STD_PAL_G | + V4L2_STD_PAL_H | + V4L2_STD_PAL_I | V4L2_STD_PAL_N | + (((v4l2_std_id)0x01) << 32)), + .name = "PAL_BGHIN_SLOW", + .frameperiod = {1, 5}, + .framelines = 625, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & NTSC_N_443_SLOW), + .v4l2_standard = { + .index = NTSC_N_443_SLOW, + .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x11) << 32)), + .name = "NTSC_N_443_SLOW", + .frameperiod = {1, 5}, + .framelines = 480, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & PAL_Nc_SLOW), + .v4l2_standard = { + .index = PAL_Nc_SLOW, + .id = (V4L2_STD_PAL_Nc | (((v4l2_std_id)0x01) << 32)), + .name = "PAL_Nc_SLOW", + .frameperiod = {1, 5}, + .framelines = 625, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & NTSC_N_SLOW), + .v4l2_standard = { + .index = NTSC_N_SLOW, + .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x21) << 32)), + .name = "NTSC_N_SLOW", + .frameperiod = {1, 5}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & SECAM_SLOW), + .v4l2_standard = { + .index = SECAM_SLOW, + .id = (V4L2_STD_SECAM | (((v4l2_std_id)0x01) << 32)), + .name = "SECAM_SLOW", + .frameperiod = {1, 5}, + .framelines = 625, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & NTSC_M_SLOW), + .v4l2_standard = { + .index = NTSC_M_SLOW, + .id = (V4L2_STD_NTSC_M | (((v4l2_std_id)0x01) << 32)), + .name = "NTSC_M_SLOW", + .frameperiod = {1, 6}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & NTSC_M_JP_SLOW), + .v4l2_standard = { + .index = NTSC_M_JP_SLOW, + .id = (V4L2_STD_NTSC_M_JP | + (((v4l2_std_id)0x01) << 32)), + .name = "NTSC_M_JP_SLOW", + .frameperiod = {1, 6}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & PAL_60_SLOW), + .v4l2_standard = { + .index = PAL_60_SLOW, + .id = (V4L2_STD_PAL_60 | (((v4l2_std_id)0x01) << 32)), + .name = "PAL_60_SLOW", + .frameperiod = {1, 6}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & NTSC_443_SLOW), + .v4l2_standard = { + .index = NTSC_443_SLOW, + .id = (V4L2_STD_NTSC_443 | (((v4l2_std_id)0x01) << 32)), + .name = "NTSC_443_SLOW", + .frameperiod = {1, 6}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0x8000 | (0x00FF & PAL_M_SLOW), + .v4l2_standard = { + .index = PAL_M_SLOW, + .id = (V4L2_STD_PAL_M | (((v4l2_std_id)0x01) << 32)), + .name = "PAL_M_SLOW", + .frameperiod = {1, 6}, + .framelines = 525, + .reserved = {0, 0, 0, 0} + } + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .mask = 0xFFFF + } +}; +/*---------------------------------------------------------------------------*/ +/* + * THE 16-BIT easycap_format.mask HAS MEANING: + * (least significant) BIT 0: 0 => PAL, 25 FPS; 1 => NTSC, 30 FPS + * BITS 2-4: RESERVED FOR DIFFERENTIATING STANDARDS + * BITS 5-7: NUMBER OF BYTES PER PIXEL + * BIT 8: 0 => NATIVE BYTE ORDER; 1 => SWAPPED + * BITS 9-10: RESERVED FOR OTHER BYTE PERMUTATIONS + * BIT 11: 0 => UNDECIMATED; 1 => DECIMATED + * BIT 12: 0 => OFFER FRAMES; 1 => OFFER FIELDS + * BIT 13: 0 => FULL FRAMERATE; 1 => REDUCED + * (most significant) BITS 14-15: RESERVED FOR OTHER FIELD/FRAME OPTIONS + * IT FOLLOWS THAT: + * bytesperpixel IS ((0x00E0 & easycap_format.mask) >> 5) + * byteswaporder IS true IF (0 != (0x0100 & easycap_format.mask)) + * + * decimatepixel IS true IF (0 != (0x0800 & easycap_format.mask)) + * + * offerfields IS true IF (0 != (0x1000 & easycap_format.mask)) + */ +/*---------------------------------------------------------------------------*/ + +struct easycap_format easycap_format[1 + SETTINGS_MANY]; + +int fillin_formats(void) +{ + const char *name1, *name2, *name3, *name4; + struct v4l2_format *fmt; + int i, j, k, m, n; + u32 width, height, pixelformat, bytesperline, sizeimage; + u16 mask1, mask2, mask3, mask4; + enum v4l2_field field; + enum v4l2_colorspace colorspace; + + for (i = 0, n = 0; i < STANDARD_MANY; i++) { + mask1 = 0x0000; + switch (i) { + case PAL_BGHIN: { + mask1 = 0x1F & PAL_BGHIN; + name1 = "PAL_BGHIN"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case SECAM: { + mask1 = 0x1F & SECAM; + name1 = "SECAM"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case PAL_Nc: { + mask1 = 0x1F & PAL_Nc; + name1 = "PAL_Nc"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case PAL_60: { + mask1 = 0x1F & PAL_60; + name1 = "PAL_60"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case PAL_M: { + mask1 = 0x1F & PAL_M; + name1 = "PAL_M"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case NTSC_M: { + mask1 = 0x1F & NTSC_M; + name1 = "NTSC_M"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_443: { + mask1 = 0x1F & NTSC_443; + name1 = "NTSC_443"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_M_JP: { + mask1 = 0x1F & NTSC_M_JP; + name1 = "NTSC_M_JP"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_N: { + mask1 = 0x1F & NTSC_M; + name1 = "NTSC_N"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_N_443: { + mask1 = 0x1F & NTSC_N_443; + name1 = "NTSC_N_443"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case PAL_BGHIN_SLOW: { + mask1 = 0x001F & PAL_BGHIN_SLOW; + mask1 |= 0x0200; + name1 = "PAL_BGHIN_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case SECAM_SLOW: { + mask1 = 0x001F & SECAM_SLOW; + mask1 |= 0x0200; + name1 = "SECAM_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case PAL_Nc_SLOW: { + mask1 = 0x001F & PAL_Nc_SLOW; + mask1 |= 0x0200; + name1 = "PAL_Nc_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case PAL_60_SLOW: { + mask1 = 0x001F & PAL_60_SLOW; + mask1 |= 0x0200; + name1 = "PAL_60_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case PAL_M_SLOW: { + mask1 = 0x001F & PAL_M_SLOW; + mask1 |= 0x0200; + name1 = "PAL_M_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_BG; + break; + } + case NTSC_M_SLOW: { + mask1 = 0x001F & NTSC_M_SLOW; + mask1 |= 0x0200; + name1 = "NTSC_M_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_443_SLOW: { + mask1 = 0x001F & NTSC_443_SLOW; + mask1 |= 0x0200; + name1 = "NTSC_443_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_M_JP_SLOW: { + mask1 = 0x001F & NTSC_M_JP_SLOW; + mask1 |= 0x0200; + name1 = "NTSC_M_JP_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_N_SLOW: { + mask1 = 0x001F & NTSC_N_SLOW; + mask1 |= 0x0200; + name1 = "NTSC_N_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + case NTSC_N_443_SLOW: { + mask1 = 0x001F & NTSC_N_443_SLOW; + mask1 |= 0x0200; + name1 = "NTSC_N_443_SLOW"; + colorspace = V4L2_COLORSPACE_470_SYSTEM_M; + break; + } + default: + return -1; + } + + for (j = 0; j < RESOLUTION_MANY; j++) { + mask2 = 0x0000; + switch (j) { + case AT_720x576: { + if (0x1 & mask1) + continue; + name2 = "_AT_720x576"; + width = 720; + height = 576; + break; + } + case AT_704x576: { + if (0x1 & mask1) + continue; + name2 = "_AT_704x576"; + width = 704; + height = 576; + break; + } + case AT_640x480: { + name2 = "_AT_640x480"; + width = 640; + height = 480; + break; + } + case AT_720x480: { + if (!(0x1 & mask1)) + continue; + name2 = "_AT_720x480"; + width = 720; + height = 480; + break; + } + case AT_360x288: { + if (0x1 & mask1) + continue; + name2 = "_AT_360x288"; + width = 360; + height = 288; + mask2 = 0x0800; + break; + } + case AT_320x240: { + name2 = "_AT_320x240"; + width = 320; + height = 240; + mask2 = 0x0800; + break; + } + case AT_360x240: { + if (!(0x1 & mask1)) + continue; + name2 = "_AT_360x240"; + width = 360; + height = 240; + mask2 = 0x0800; + break; + } + default: + return -2; + } + + for (k = 0; k < PIXELFORMAT_MANY; k++) { + mask3 = 0x0000; + switch (k) { + case FMT_UYVY: { + name3 = __stringify(FMT_UYVY); + pixelformat = V4L2_PIX_FMT_UYVY; + mask3 |= (0x02 << 5); + break; + } + case FMT_YUY2: { + name3 = __stringify(FMT_YUY2); + pixelformat = V4L2_PIX_FMT_YUYV; + mask3 |= (0x02 << 5); + mask3 |= 0x0100; + break; + } + case FMT_RGB24: { + name3 = __stringify(FMT_RGB24); + pixelformat = V4L2_PIX_FMT_RGB24; + mask3 |= (0x03 << 5); + break; + } + case FMT_RGB32: { + name3 = __stringify(FMT_RGB32); + pixelformat = V4L2_PIX_FMT_RGB32; + mask3 |= (0x04 << 5); + break; + } + case FMT_BGR24: { + name3 = __stringify(FMT_BGR24); + pixelformat = V4L2_PIX_FMT_BGR24; + mask3 |= (0x03 << 5); + mask3 |= 0x0100; + break; + } + case FMT_BGR32: { + name3 = __stringify(FMT_BGR32); + pixelformat = V4L2_PIX_FMT_BGR32; + mask3 |= (0x04 << 5); + mask3 |= 0x0100; + break; + } + default: + return -3; + } + bytesperline = width * ((mask3 & 0x00E0) >> 5); + sizeimage = bytesperline * height; + + for (m = 0; m < INTERLACE_MANY; m++) { + mask4 = 0x0000; + switch (m) { + case FIELD_NONE: { + name4 = "-n"; + field = V4L2_FIELD_NONE; + break; + } + case FIELD_INTERLACED: { + name4 = "-i"; + mask4 |= 0x1000; + field = V4L2_FIELD_INTERLACED; + break; + } + default: + return -4; + } + if (SETTINGS_MANY <= n) + return -5; + + strcpy(easycap_format[n].name, name1); + strcat(easycap_format[n].name, name2); + strcat(easycap_format[n].name, "_"); + strcat(easycap_format[n].name, name3); + strcat(easycap_format[n].name, name4); + easycap_format[n].mask = + mask1 | mask2 | mask3 | mask4; + fmt = &easycap_format[n].v4l2_format; + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->fmt.pix.width = width; + fmt->fmt.pix.height = height; + fmt->fmt.pix.pixelformat = pixelformat; + fmt->fmt.pix.field = field; + fmt->fmt.pix.bytesperline = bytesperline; + fmt->fmt.pix.sizeimage = sizeimage; + fmt->fmt.pix.colorspace = colorspace; + fmt->fmt.pix.priv = 0; + n++; + } + } + } + } + if ((1 + SETTINGS_MANY) <= n) + return -6; + easycap_format[n].mask = 0xFFFF; + return n; +} +/*---------------------------------------------------------------------------*/ +struct v4l2_queryctrl easycap_control[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = SAA_0A_DEFAULT, + .flags = 0, + .reserved = {0, 0} + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = SAA_0B_DEFAULT + 128, + .flags = 0, + .reserved = {0, 0} + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = SAA_0C_DEFAULT + 128, + .flags = 0, + .reserved = {0, 0} + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = SAA_0D_DEFAULT + 128, + .flags = 0, + .reserved = {0, 0} + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .id = V4L2_CID_AUDIO_VOLUME, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Volume", + .minimum = 0, + .maximum = 31, + .step = 1, + .default_value = 16, + .flags = 0, + .reserved = {0, 0} + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .id = V4L2_CID_AUDIO_MUTE, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Mute", + .default_value = true, + .flags = 0, + .reserved = {0, 0} + }, +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + { + .id = 0xFFFFFFFF + } +}; +/*****************************************************************************/ diff --git a/drivers/staging/media/easycap/easycap_sound.c b/drivers/staging/media/easycap/easycap_sound.c new file mode 100644 index 000000000000..b22bb39b5f69 --- /dev/null +++ b/drivers/staging/media/easycap/easycap_sound.c @@ -0,0 +1,816 @@ +/****************************************************************************** +* * +* easycap_sound.c * +* * +* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 * +* * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +/*****************************************************************************/ + +#include "easycap.h" + +/*--------------------------------------------------------------------------*/ +/* + * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE + */ +/*--------------------------------------------------------------------------*/ +static const struct snd_pcm_hardware alsa_hardware = { + .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_32000 | SNDRV_PCM_RATE_48000, + .rate_min = 32000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = PAGE_SIZE * + PAGES_PER_AUDIO_FRAGMENT * + AUDIO_FRAGMENT_MANY, + .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT, + .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2, + .periods_min = AUDIO_FRAGMENT_MANY, + .periods_max = AUDIO_FRAGMENT_MANY * 2, +}; + + +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER + * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE, + * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO. + */ +/*---------------------------------------------------------------------------*/ +void +easycap_alsa_complete(struct urb *purb) +{ + struct easycap *peasycap; + struct snd_pcm_substream *pss; + struct snd_pcm_runtime *prt; + int dma_bytes, fragment_bytes; + int isfragment; + u8 *p1, *p2; + s16 tmp; + int i, j, more, much, rc; +#ifdef UPSAMPLE + int k; + s16 oldaudio, newaudio, delta; +#endif /*UPSAMPLE*/ + + JOT(16, "\n"); + + if (!purb) { + SAY("ERROR: purb is NULL\n"); + return; + } + peasycap = purb->context; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return; + } + much = 0; + if (peasycap->audio_idle) { + JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n", + peasycap->audio_idle, peasycap->audio_isoc_streaming); + if (peasycap->audio_isoc_streaming) + goto resubmit; + } +/*---------------------------------------------------------------------------*/ + pss = peasycap->psubstream; + if (!pss) + goto resubmit; + prt = pss->runtime; + if (!prt) + goto resubmit; + dma_bytes = (int)prt->dma_bytes; + if (0 == dma_bytes) + goto resubmit; + fragment_bytes = 4 * ((int)prt->period_size); + if (0 == fragment_bytes) + goto resubmit; +/* -------------------------------------------------------------------------*/ + if (purb->status) { + if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) { + JOM(16, "urb status -ESHUTDOWN or -ENOENT\n"); + return; + } + SAM("ERROR: non-zero urb status: -%s: %d\n", + strerror(purb->status), purb->status); + goto resubmit; + } +/*---------------------------------------------------------------------------*/ +/* + * PROCEED HERE WHEN NO ERROR + */ +/*---------------------------------------------------------------------------*/ + +#ifdef UPSAMPLE + oldaudio = peasycap->oldaudio; +#endif /*UPSAMPLE*/ + + for (i = 0; i < purb->number_of_packets; i++) { + if (purb->iso_frame_desc[i].status < 0) { + SAM("-%s: %d\n", + strerror(purb->iso_frame_desc[i].status), + purb->iso_frame_desc[i].status); + } + if (purb->iso_frame_desc[i].status) { + JOM(12, "discarding audio samples because " + "%i=purb->iso_frame_desc[i].status\n", + purb->iso_frame_desc[i].status); + continue; + } + more = purb->iso_frame_desc[i].actual_length; + if (more == 0) { + peasycap->audio_mt++; + continue; + } + if (0 > more) { + SAM("MISTAKE: more is negative\n"); + return; + } + + if (peasycap->audio_mt) { + JOM(12, "%4i empty audio urb frames\n", + peasycap->audio_mt); + peasycap->audio_mt = 0; + } + + p1 = (u8 *)(purb->transfer_buffer + + purb->iso_frame_desc[i].offset); + + /* + * COPY more BYTES FROM ISOC BUFFER + * TO THE DMA BUFFER, CONVERTING + * 8-BIT MONO TO 16-BIT SIGNED + * LITTLE-ENDIAN SAMPLES IF NECESSARY + */ + while (more) { + much = dma_bytes - peasycap->dma_fill; + if (0 > much) { + SAM("MISTAKE: much is negative\n"); + return; + } + if (0 == much) { + peasycap->dma_fill = 0; + peasycap->dma_next = fragment_bytes; + JOM(8, "wrapped dma buffer\n"); + } + if (!peasycap->microphone) { + if (much > more) + much = more; + memcpy(prt->dma_area + peasycap->dma_fill, + p1, much); + p1 += much; + more -= much; + } else { +#ifdef UPSAMPLE + if (much % 16) + JOM(8, "MISTAKE? much" + " is not divisible by 16\n"); + if (much > (16 * more)) + much = 16 * more; + p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); + + for (j = 0; j < (much / 16); j++) { + newaudio = ((int) *p1) - 128; + newaudio = 128 * newaudio; + + delta = (newaudio - oldaudio) / 4; + tmp = oldaudio + delta; + + for (k = 0; k < 4; k++) { + *p2 = (0x00FF & tmp); + *(p2 + 1) = (0xFF00 & tmp) >> 8; + p2 += 2; + *p2 = (0x00FF & tmp); + *(p2 + 1) = (0xFF00 & tmp) >> 8; + p2 += 2; + tmp += delta; + } + p1++; + more--; + oldaudio = tmp; + } +#else /*!UPSAMPLE*/ + if (much > (2 * more)) + much = 2 * more; + p2 = (u8 *)(prt->dma_area + peasycap->dma_fill); + + for (j = 0; j < (much / 2); j++) { + tmp = ((int) *p1) - 128; + tmp = 128 * tmp; + *p2 = (0x00FF & tmp); + *(p2 + 1) = (0xFF00 & tmp) >> 8; + p1++; + p2 += 2; + more--; + } +#endif /*UPSAMPLE*/ + } + peasycap->dma_fill += much; + if (peasycap->dma_fill >= peasycap->dma_next) { + isfragment = peasycap->dma_fill / fragment_bytes; + if (0 > isfragment) { + SAM("MISTAKE: isfragment is negative\n"); + return; + } + peasycap->dma_read = (isfragment - 1) * fragment_bytes; + peasycap->dma_next = (isfragment + 1) * fragment_bytes; + if (dma_bytes < peasycap->dma_next) + peasycap->dma_next = fragment_bytes; + + if (0 <= peasycap->dma_read) { + JOM(8, "snd_pcm_period_elapsed(), %i=" + "isfragment\n", isfragment); + snd_pcm_period_elapsed(pss); + } + } + } + +#ifdef UPSAMPLE + peasycap->oldaudio = oldaudio; +#endif /*UPSAMPLE*/ + + } +/*---------------------------------------------------------------------------*/ +/* + * RESUBMIT THIS URB + */ +/*---------------------------------------------------------------------------*/ +resubmit: + if (peasycap->audio_isoc_streaming == 0) + return; + + rc = usb_submit_urb(purb, GFP_ATOMIC); + if (rc) { + if ((-ENODEV != rc) && (-ENOENT != rc)) { + SAM("ERROR: while %i=audio_idle, usb_submit_urb failed " + "with rc: -%s :%d\n", + peasycap->audio_idle, strerror(rc), rc); + } + if (0 < peasycap->audio_isoc_streaming) + peasycap->audio_isoc_streaming--; + } + return; +} +/*****************************************************************************/ +static int easycap_alsa_open(struct snd_pcm_substream *pss) +{ + struct snd_pcm *psnd_pcm; + struct snd_card *psnd_card; + struct easycap *peasycap; + + JOT(4, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + psnd_pcm = pss->pcm; + if (!psnd_pcm) { + SAY("ERROR: psnd_pcm is NULL\n"); + return -EFAULT; + } + psnd_card = psnd_pcm->card; + if (!psnd_card) { + SAY("ERROR: psnd_card is NULL\n"); + return -EFAULT; + } + + peasycap = psnd_card->private_data; + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (peasycap->psnd_card != psnd_card) { + SAM("ERROR: bad peasycap->psnd_card\n"); + return -EFAULT; + } + if (peasycap->psubstream) { + SAM("ERROR: bad peasycap->psubstream\n"); + return -EFAULT; + } + pss->private_data = peasycap; + peasycap->psubstream = pss; + pss->runtime->hw = peasycap->alsa_hardware; + pss->runtime->private_data = peasycap; + pss->private_data = peasycap; + + if (0 != easycap_sound_setup(peasycap)) { + JOM(4, "ending unsuccessfully\n"); + return -EFAULT; + } + JOM(4, "ending successfully\n"); + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_close(struct snd_pcm_substream *pss) +{ + struct easycap *peasycap; + + JOT(4, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + pss->private_data = NULL; + peasycap->psubstream = NULL; + JOT(4, "ending successfully\n"); + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz) +{ + struct snd_pcm_runtime *prt; + JOT(4, "\n"); + + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + prt = pss->runtime; + if (!prt) { + SAY("ERROR: substream.runtime is NULL\n"); + return -EFAULT; + } + if (prt->dma_area) { + if (prt->dma_bytes > sz) + return 0; + vfree(prt->dma_area); + } + prt->dma_area = vmalloc(sz); + if (!prt->dma_area) + return -ENOMEM; + prt->dma_bytes = sz; + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_hw_params(struct snd_pcm_substream *pss, + struct snd_pcm_hw_params *phw) +{ + int rc; + + JOT(4, "%i\n", (params_buffer_bytes(phw))); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw)); + if (rc) + return rc; + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_hw_free(struct snd_pcm_substream *pss) +{ + struct snd_pcm_runtime *prt; + JOT(4, "\n"); + + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + prt = pss->runtime; + if (!prt) { + SAY("ERROR: substream.runtime is NULL\n"); + return -EFAULT; + } + if (prt->dma_area) { + JOT(8, "prt->dma_area = %p\n", prt->dma_area); + vfree(prt->dma_area); + prt->dma_area = NULL; + } else + JOT(8, "dma_area already freed\n"); + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_prepare(struct snd_pcm_substream *pss) +{ + struct easycap *peasycap; + struct snd_pcm_runtime *prt; + + JOT(4, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + prt = pss->runtime; + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + + JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate); + JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size); + JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods); + JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size); + JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes); + JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary); + JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step); + JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits); + JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits); + JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align); + JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base); + JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n", + pss->runtime->hw_ptr_interrupt); + + if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) { + SAY("MISTAKE: unexpected ALSA parameters\n"); + return -ENOENT; + } + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_ack(struct snd_pcm_substream *pss) +{ + return 0; +} +/*****************************************************************************/ +static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd) +{ + struct easycap *peasycap; + int retval; + + JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START, + SNDRV_PCM_TRIGGER_STOP); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: { + peasycap->audio_idle = 0; + break; + } + case SNDRV_PCM_TRIGGER_STOP: { + peasycap->audio_idle = 1; + break; + } + default: + retval = -EINVAL; + } + return 0; +} +/*****************************************************************************/ +static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss) +{ + struct easycap *peasycap; + snd_pcm_uframes_t offset; + + JOT(16, "\n"); + if (!pss) { + SAY("ERROR: pss is NULL\n"); + return -EFAULT; + } + peasycap = snd_pcm_substream_chip(pss); + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) { + JOM(8, "returning -EIO because " + "%i=audio_idle %i=audio_eof\n", + peasycap->audio_idle, peasycap->audio_eof); + return -EIO; + } +/*---------------------------------------------------------------------------*/ + if (0 > peasycap->dma_read) { + JOM(8, "returning -EBUSY\n"); + return -EBUSY; + } + offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4; + JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base); + JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n", + (int)pss->runtime->hw_ptr_interrupt); + JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n", + (int)offset, peasycap->dma_read, peasycap->dma_next); + return offset; +} +/*****************************************************************************/ +static struct page * +easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset) +{ + return vmalloc_to_page(pss->runtime->dma_area + offset); +} +/*****************************************************************************/ + +static struct snd_pcm_ops easycap_alsa_pcm_ops = { + .open = easycap_alsa_open, + .close = easycap_alsa_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = easycap_alsa_hw_params, + .hw_free = easycap_alsa_hw_free, + .prepare = easycap_alsa_prepare, + .ack = easycap_alsa_ack, + .trigger = easycap_alsa_trigger, + .pointer = easycap_alsa_pointer, + .page = easycap_alsa_page, +}; + +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS + * MEANS MODULE easycap. BEWARE. +*/ +/*---------------------------------------------------------------------------*/ +int easycap_alsa_probe(struct easycap *peasycap) +{ + int rc; + struct snd_card *psnd_card; + struct snd_pcm *psnd_pcm; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -ENODEV; + } + if (0 > peasycap->minor) { + SAY("ERROR: no minor\n"); + return -ENODEV; + } + + peasycap->alsa_hardware = alsa_hardware; + if (peasycap->microphone) { + peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000; + peasycap->alsa_hardware.rate_min = 32000; + peasycap->alsa_hardware.rate_max = 32000; + } else { + peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000; + peasycap->alsa_hardware.rate_min = 48000; + peasycap->alsa_hardware.rate_max = 48000; + } + + if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa", + THIS_MODULE, 0, &psnd_card)) { + SAY("ERROR: Cannot do ALSA snd_card_create()\n"); + return -EFAULT; + } + + sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor); + strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION); + strcpy(&psnd_card->shortname[0], "easycap_alsa"); + sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]); + + psnd_card->dev = &peasycap->pusb_device->dev; + psnd_card->private_data = peasycap; + peasycap->psnd_card = psnd_card; + + rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm); + if (rc) { + SAM("ERROR: Cannot do ALSA snd_pcm_new()\n"); + snd_card_free(psnd_card); + return -EFAULT; + } + + snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE, + &easycap_alsa_pcm_ops); + psnd_pcm->info_flags = 0; + strcpy(&psnd_pcm->name[0], &psnd_card->id[0]); + psnd_pcm->private_data = peasycap; + peasycap->psnd_pcm = psnd_pcm; + peasycap->psubstream = NULL; + + rc = snd_card_register(psnd_card); + if (rc) { + SAM("ERROR: Cannot do ALSA snd_card_register()\n"); + snd_card_free(psnd_card); + return -EFAULT; + } + + SAM("registered %s\n", &psnd_card->id[0]); + return 0; +} + +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * COMMON AUDIO INITIALIZATION + */ +/*---------------------------------------------------------------------------*/ +int +easycap_sound_setup(struct easycap *peasycap) +{ + int rc; + + JOM(4, "starting initialization\n"); + + if (!peasycap) { + SAY("ERROR: peasycap is NULL.\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -ENODEV; + } + JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device); + + rc = audio_setup(peasycap); + JOM(8, "audio_setup() returned %i\n", rc); + + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device has become NULL\n"); + return -ENODEV; + } +/*---------------------------------------------------------------------------*/ + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device has become NULL\n"); + return -ENODEV; + } + rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface, + peasycap->audio_altsetting_on); + JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface, + peasycap->audio_altsetting_on, rc); + + rc = wakeup_device(peasycap->pusb_device); + JOM(8, "wakeup_device() returned %i\n", rc); + + peasycap->audio_eof = 0; + peasycap->audio_idle = 0; + + submit_audio_urbs(peasycap); + + JOM(4, "finished initialization\n"); + return 0; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * SUBMIT ALL AUDIO URBS. + */ +/*---------------------------------------------------------------------------*/ +int +submit_audio_urbs(struct easycap *peasycap) +{ + struct data_urb *pdata_urb; + struct urb *purb; + struct list_head *plist_head; + int j, isbad, nospc, m, rc; + int isbuf; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + if (!peasycap->purb_audio_head) { + SAM("ERROR: peasycap->urb_audio_head uninitialized\n"); + return -EFAULT; + } + if (!peasycap->pusb_device) { + SAM("ERROR: peasycap->pusb_device is NULL\n"); + return -EFAULT; + } + + if (peasycap->audio_isoc_streaming) { + JOM(4, "already streaming audio urbs\n"); + return 0; + } + + JOM(4, "initial submission of all audio urbs\n"); + rc = usb_set_interface(peasycap->pusb_device, + peasycap->audio_interface, + peasycap->audio_altsetting_on); + JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", + peasycap->audio_interface, + peasycap->audio_altsetting_on, rc); + + isbad = 0; + nospc = 0; + m = 0; + list_for_each(plist_head, peasycap->purb_audio_head) { + pdata_urb = list_entry(plist_head, struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + purb = pdata_urb->purb; + isbuf = pdata_urb->isbuf; + + purb->interval = 1; + purb->dev = peasycap->pusb_device; + purb->pipe = usb_rcvisocpipe(peasycap->pusb_device, + peasycap->audio_endpointnumber); + purb->transfer_flags = URB_ISO_ASAP; + purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo; + purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size; + purb->complete = easycap_alsa_complete; + purb->context = peasycap; + purb->start_frame = 0; + purb->number_of_packets = peasycap->audio_isoc_framesperdesc; + for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) { + purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize; + purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize; + } + + rc = usb_submit_urb(purb, GFP_KERNEL); + if (rc) { + isbad++; + SAM("ERROR: usb_submit_urb() failed" + " for urb with rc: -%s: %d\n", + strerror(rc), rc); + } else { + m++; + } + } else { + isbad++; + } + } + if (nospc) { + SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc); + SAM("..... possibly inadequate USB bandwidth\n"); + peasycap->audio_eof = 1; + } + if (isbad) { + JOM(4, "attempting cleanup instead of submitting\n"); + list_for_each(plist_head, (peasycap->purb_audio_head)) { + pdata_urb = list_entry(plist_head, struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) + usb_kill_urb(pdata_urb->purb); + } + peasycap->audio_isoc_streaming = 0; + } else { + peasycap->audio_isoc_streaming = m; + JOM(4, "submitted %i audio urbs\n", m); + } + + return 0; +} +/*****************************************************************************/ +/*---------------------------------------------------------------------------*/ +/* + * KILL ALL AUDIO URBS. + */ +/*---------------------------------------------------------------------------*/ +int +kill_audio_urbs(struct easycap *peasycap) +{ + int m; + struct list_head *plist_head; + struct data_urb *pdata_urb; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return -EFAULT; + } + + if (!peasycap->audio_isoc_streaming) { + JOM(8, "%i=audio_isoc_streaming, no audio urbs killed\n", + peasycap->audio_isoc_streaming); + return 0; + } + + if (!peasycap->purb_audio_head) { + SAM("ERROR: peasycap->purb_audio_head is NULL\n"); + return -EFAULT; + } + + peasycap->audio_isoc_streaming = 0; + JOM(4, "killing audio urbs\n"); + m = 0; + list_for_each(plist_head, (peasycap->purb_audio_head)) { + pdata_urb = list_entry(plist_head, struct data_urb, list_head); + if (pdata_urb && pdata_urb->purb) { + usb_kill_urb(pdata_urb->purb); + m++; + } + } + JOM(4, "%i audio urbs killed\n", m); + + return 0; +} +/*****************************************************************************/ diff --git a/drivers/staging/media/easycap/easycap_testcard.c b/drivers/staging/media/easycap/easycap_testcard.c new file mode 100644 index 000000000000..0f71470ace39 --- /dev/null +++ b/drivers/staging/media/easycap/easycap_testcard.c @@ -0,0 +1,155 @@ +/****************************************************************************** +* * +* easycap_testcard.c * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * +*/ +/*****************************************************************************/ + +#include "easycap.h" + +/*****************************************************************************/ +#define TESTCARD_BYTESPERLINE (2 * 720) +void +easycap_testcard(struct easycap *peasycap, int field) +{ + int total; + int y, u, v, r, g, b; + unsigned char uyvy[4]; + int i1, line, k, m, n, more, much, barwidth, barheight; + unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2; + struct data_buffer *pfield_buffer; + + if (!peasycap) { + SAY("ERROR: peasycap is NULL\n"); + return; + } + JOM(8, "%i=field\n", field); + switch (peasycap->width) { + case 720: + case 360: { + barwidth = (2 * 720) / 8; + break; + } + case 704: + case 352: { + barwidth = (2 * 704) / 8; + break; + } + case 640: + case 320: { + barwidth = (2 * 640) / 8; + break; + } + default: { + SAM("ERROR: cannot set barwidth\n"); + return; + } + } + if (TESTCARD_BYTESPERLINE < barwidth) { + SAM("ERROR: barwidth is too large\n"); + return; + } + switch (peasycap->height) { + case 576: + case 288: { + barheight = 576; + break; + } + case 480: + case 240: { + barheight = 480; + break; + } + default: { + SAM("ERROR: cannot set barheight\n"); + return; + } + } + total = 0; + k = field; + m = 0; + n = 0; + + for (line = 0; line < (barheight / 2); line++) { + for (i1 = 0; i1 < 8; i1++) { + r = (i1 * 256)/8; + g = (i1 * 256)/8; + b = (i1 * 256)/8; + + y = 299*r/1000 + 587*g/1000 + 114*b/1000 ; + u = -147*r/1000 - 289*g/1000 + 436*b/1000 ; + u = u + 128; + v = 615*r/1000 - 515*g/1000 - 100*b/1000 ; + v = v + 128; + + uyvy[0] = 0xFF & u ; + uyvy[1] = 0xFF & y ; + uyvy[2] = 0xFF & v ; + uyvy[3] = 0xFF & y ; + + p1 = &bfbar[0]; + while (p1 < &bfbar[barwidth]) { + *p1++ = uyvy[0] ; + *p1++ = uyvy[1] ; + *p1++ = uyvy[2] ; + *p1++ = uyvy[3] ; + total += 4; + } + + p1 = &bfbar[0]; + more = barwidth; + + while (more) { + if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) { + SAM("ERROR: bad m reached\n"); + return; + } + if (PAGE_SIZE < n) { + SAM("ERROR: bad n reached\n"); + return; + } + + if (0 > more) { + SAM("ERROR: internal fault\n"); + return; + } + + much = PAGE_SIZE - n; + if (much > more) + much = more; + pfield_buffer = &peasycap->field_buffer[k][m]; + p2 = pfield_buffer->pgo + n; + memcpy(p2, p1, much); + + p1 += much; + n += much; + more -= much; + if (PAGE_SIZE == n) { + m++; + n = 0; + } + } + } + } + return; +} diff --git a/drivers/staging/media/go7007/Kconfig b/drivers/staging/media/go7007/Kconfig new file mode 100644 index 000000000000..7dfb2815b9ec --- /dev/null +++ b/drivers/staging/media/go7007/Kconfig @@ -0,0 +1,109 @@ +config VIDEO_GO7007 + tristate "WIS GO7007 MPEG encoder support" + depends on VIDEO_DEV && PCI && I2C + depends on SND + select VIDEOBUF_DMA_SG + depends on RC_CORE + select VIDEO_TUNER + select VIDEO_TVEEPROM + select SND_PCM + select CRC32 + default N + ---help--- + This is a video4linux driver for the WIS GO7007 MPEG + encoder chip. + + To compile this driver as a module, choose M here: the + module will be called go7007 + +config VIDEO_GO7007_USB + tristate "WIS GO7007 USB support" + depends on VIDEO_GO7007 && USB + default N + ---help--- + This is a video4linux driver for the WIS GO7007 MPEG + encoder chip over USB. + + To compile this driver as a module, choose M here: the + module will be called go7007-usb + +config VIDEO_GO7007_USB_S2250_BOARD + tristate "Sensoray 2250/2251 support" + depends on VIDEO_GO7007_USB && DVB_USB + default N + ---help--- + This is a video4linux driver for the Sensoray 2250/2251 device. + + To compile this driver as a module, choose M here: the + module will be called s2250 + +config VIDEO_GO7007_OV7640 + tristate "OV7640 subdev support" + depends on VIDEO_GO7007 + default N + ---help--- + This is a video4linux driver for the OV7640 sub-device. + + To compile this driver as a module, choose M here: the + module will be called wis-ov7640 + +config VIDEO_GO7007_SAA7113 + tristate "SAA7113 subdev support" + depends on VIDEO_GO7007 + default N + ---help--- + This is a video4linux driver for the SAA7113 sub-device. + + To compile this driver as a module, choose M here: the + module will be called wis-saa7113 + +config VIDEO_GO7007_SAA7115 + tristate "SAA7115 subdev support" + depends on VIDEO_GO7007 + default N + ---help--- + This is a video4linux driver for the SAA7115 sub-device. + + To compile this driver as a module, choose M here: the + module will be called wis-saa7115 + +config VIDEO_GO7007_TW9903 + tristate "TW9903 subdev support" + depends on VIDEO_GO7007 + default N + ---help--- + This is a video4linux driver for the TW9903 sub-device. + + To compile this driver as a module, choose M here: the + module will be called wis-tw9903 + +config VIDEO_GO7007_UDA1342 + tristate "UDA1342 subdev support" + depends on VIDEO_GO7007 + default N + ---help--- + This is a video4linux driver for the UDA1342 sub-device. + + To compile this driver as a module, choose M here: the + module will be called wis-uda1342 + +config VIDEO_GO7007_SONY_TUNER + tristate "Sony tuner subdev support" + depends on VIDEO_GO7007 + default N + ---help--- + This is a video4linux driver for the Sony Tuner sub-device. + + To compile this driver as a module, choose M here: the + module will be called wis-sony-tuner + +config VIDEO_GO7007_TW2804 + tristate "TW2804 subdev support" + depends on VIDEO_GO7007 + default N + ---help--- + This is a video4linux driver for the TW2804 sub-device. + + To compile this driver as a module, choose M here: the + module will be called wis-tw2804 + diff --git a/drivers/staging/media/go7007/Makefile b/drivers/staging/media/go7007/Makefile new file mode 100644 index 000000000000..6ee837c56706 --- /dev/null +++ b/drivers/staging/media/go7007/Makefile @@ -0,0 +1,30 @@ +#obj-m += go7007.o go7007-usb.o snd-go7007.o wis-saa7115.o wis-tw9903.o \ + wis-uda1342.o wis-sony-tuner.o wis-saa7113.o wis-ov7640.o \ + wis-tw2804.o + + +obj-$(CONFIG_VIDEO_GO7007) += go7007.o +obj-$(CONFIG_VIDEO_GO7007_USB) += go7007-usb.o +obj-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD) += s2250.o s2250-loader.o +obj-$(CONFIG_VIDEO_GO7007_SAA7113) += wis-saa7113.o +obj-$(CONFIG_VIDEO_GO7007_OV7640) += wis-ov7640.o +obj-$(CONFIG_VIDEO_GO7007_SAA7115) += wis-saa7115.o +obj-$(CONFIG_VIDEO_GO7007_TW9903) += wis-tw9903.o +obj-$(CONFIG_VIDEO_GO7007_UDA1342) += wis-uda1342.o +obj-$(CONFIG_VIDEO_GO7007_SONY_TUNER) += wis-sony-tuner.o +obj-$(CONFIG_VIDEO_GO7007_TW2804) += wis-tw2804.o + +go7007-y := go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ + snd-go7007.o + +s2250-y := s2250-board.o + +# Uncomment when the saa7134 patches get into upstream +#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o +#ccflags-$(CONFIG_VIDEO_SAA7134:m=y) += -Idrivers/media/video/saa7134 -DSAA7134_MPEG_GO7007=3 + +# S2250 needs cypress ezusb loader from dvb-usb +ccflags-$(CONFIG_VIDEO_GO7007_USB_S2250_BOARD:m=y) += -Idrivers/media/dvb/dvb-usb + +ccflags-y += -Idrivers/media/dvb/frontends +ccflags-y += -Idrivers/media/dvb/dvb-core diff --git a/drivers/staging/media/go7007/README b/drivers/staging/media/go7007/README new file mode 100644 index 000000000000..48f447637817 --- /dev/null +++ b/drivers/staging/media/go7007/README @@ -0,0 +1,11 @@ +Todo: + - checkpatch.pl cleanups + - sparse cleanups + - lots of little modules, should be merged together + and added to the build. + - testing? + - handle churn in v4l layer. + +Please send patchs to Greg Kroah-Hartman and Cc: Ross +Cohen as well. + diff --git a/drivers/staging/media/go7007/go7007-driver.c b/drivers/staging/media/go7007/go7007-driver.c new file mode 100644 index 000000000000..6c9279a6d606 --- /dev/null +++ b/drivers/staging/media/go7007/go7007-driver.c @@ -0,0 +1,659 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" +#include "wis-i2c.h" + +/* + * Wait for an interrupt to be delivered from the GO7007SB and return + * the associated value and data. + * + * Must be called with the hw_lock held. + */ +int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data) +{ + go->interrupt_available = 0; + go->hpi_ops->read_interrupt(go); + if (wait_event_timeout(go->interrupt_waitq, + go->interrupt_available, 5*HZ) < 0) { + v4l2_err(&go->v4l2_dev, "timeout waiting for read interrupt\n"); + return -1; + } + if (!go->interrupt_available) + return -1; + go->interrupt_available = 0; + *value = go->interrupt_value & 0xfffe; + *data = go->interrupt_data; + return 0; +} +EXPORT_SYMBOL(go7007_read_interrupt); + +/* + * Read a register/address on the GO7007SB. + * + * Must be called with the hw_lock held. + */ +int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data) +{ + int count = 100; + u16 value; + + if (go7007_write_interrupt(go, 0x0010, addr) < 0) + return -EIO; + while (count-- > 0) { + if (go7007_read_interrupt(go, &value, data) == 0 && + value == 0xa000) + return 0; + } + return -EIO; +} +EXPORT_SYMBOL(go7007_read_addr); + +/* + * Send the boot firmware to the encoder, which just wakes it up and lets + * us talk to the GPIO pins and on-board I2C adapter. + * + * Must be called with the hw_lock held. + */ +static int go7007_load_encoder(struct go7007 *go) +{ + const struct firmware *fw_entry; + char fw_name[] = "go7007fw.bin"; + void *bounce; + int fw_len, rv = 0; + u16 intr_val, intr_data; + + if (request_firmware(&fw_entry, fw_name, go->dev)) { + v4l2_err(go, "unable to load firmware from file " + "\"%s\"\n", fw_name); + return -1; + } + if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { + v4l2_err(go, "file \"%s\" does not appear to be " + "go7007 firmware\n", fw_name); + release_firmware(fw_entry); + return -1; + } + fw_len = fw_entry->size - 16; + bounce = kmalloc(fw_len, GFP_KERNEL); + if (bounce == NULL) { + v4l2_err(go, "unable to allocate %d bytes for " + "firmware transfer\n", fw_len); + release_firmware(fw_entry); + return -1; + } + memcpy(bounce, fw_entry->data + 16, fw_len); + release_firmware(fw_entry); + if (go7007_interface_reset(go) < 0 || + go7007_send_firmware(go, bounce, fw_len) < 0 || + go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + (intr_val & ~0x1) != 0x5a5a) { + v4l2_err(go, "error transferring firmware\n"); + rv = -1; + } + kfree(bounce); + return rv; +} + +MODULE_FIRMWARE("go7007fw.bin"); + +/* + * Boot the encoder and register the I2C adapter if requested. Do the + * minimum initialization necessary, since the board-specific code may + * still need to probe the board ID. + * + * Must NOT be called with the hw_lock held. + */ +int go7007_boot_encoder(struct go7007 *go, int init_i2c) +{ + int ret; + + mutex_lock(&go->hw_lock); + ret = go7007_load_encoder(go); + mutex_unlock(&go->hw_lock); + if (ret < 0) + return -1; + if (!init_i2c) + return 0; + if (go7007_i2c_init(go) < 0) + return -1; + go->i2c_adapter_online = 1; + return 0; +} +EXPORT_SYMBOL(go7007_boot_encoder); + +/* + * Configure any hardware-related registers in the GO7007, such as GPIO + * pins and bus parameters, which are board-specific. This assumes + * the boot firmware has already been downloaded. + * + * Must be called with the hw_lock held. + */ +static int go7007_init_encoder(struct go7007 *go) +{ + if (go->board_info->audio_flags & GO7007_AUDIO_I2S_MASTER) { + go7007_write_addr(go, 0x1000, 0x0811); + go7007_write_addr(go, 0x1000, 0x0c11); + } + if (go->board_id == GO7007_BOARDID_MATRIX_REV) { + /* Set GPIO pin 0 to be an output (audio clock control) */ + go7007_write_addr(go, 0x3c82, 0x0001); + go7007_write_addr(go, 0x3c80, 0x00fe); + } + return 0; +} + +/* + * Send the boot firmware to the GO7007 and configure the registers. This + * is the only way to stop the encoder once it has started streaming video. + * + * Must be called with the hw_lock held. + */ +int go7007_reset_encoder(struct go7007 *go) +{ + if (go7007_load_encoder(go) < 0) + return -1; + return go7007_init_encoder(go); +} + +/* + * Attempt to instantiate an I2C client by ID, probably loading a module. + */ +static int init_i2c_module(struct i2c_adapter *adapter, const char *type, + int addr) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + struct v4l2_device *v4l2_dev = &go->v4l2_dev; + + if (v4l2_i2c_new_subdev(v4l2_dev, adapter, type, addr, NULL)) + return 0; + + printk(KERN_INFO "go7007: probing for module i2c:%s failed\n", type); + return -1; +} + +/* + * Finalize the GO7007 hardware setup, register the on-board I2C adapter + * (if used on this board), load the I2C client driver for the sensor + * (SAA7115 or whatever) and other devices, and register the ALSA and V4L2 + * interfaces. + * + * Must NOT be called with the hw_lock held. + */ +int go7007_register_encoder(struct go7007 *go) +{ + int i, ret; + + printk(KERN_INFO "go7007: registering new %s\n", go->name); + + mutex_lock(&go->hw_lock); + ret = go7007_init_encoder(go); + mutex_unlock(&go->hw_lock); + if (ret < 0) + return -1; + + /* v4l2 init must happen before i2c subdevs */ + ret = go7007_v4l2_init(go); + if (ret < 0) + return ret; + + if (!go->i2c_adapter_online && + go->board_info->flags & GO7007_BOARD_USE_ONBOARD_I2C) { + if (go7007_i2c_init(go) < 0) + return -1; + go->i2c_adapter_online = 1; + } + if (go->i2c_adapter_online) { + for (i = 0; i < go->board_info->num_i2c_devs; ++i) + init_i2c_module(&go->i2c_adapter, + go->board_info->i2c_devs[i].type, + go->board_info->i2c_devs[i].addr); + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) + i2c_clients_command(&go->i2c_adapter, + DECODER_SET_CHANNEL, &go->channel_number); + } + if (go->board_info->flags & GO7007_BOARD_HAS_AUDIO) { + go->audio_enabled = 1; + go7007_snd_init(go); + } + return 0; +} +EXPORT_SYMBOL(go7007_register_encoder); + +/* + * Send the encode firmware to the encoder, which will cause it + * to immediately start delivering the video and audio streams. + * + * Must be called with the hw_lock held. + */ +int go7007_start_encoder(struct go7007 *go) +{ + u8 *fw; + int fw_len, rv = 0, i; + u16 intr_val, intr_data; + + go->modet_enable = 0; + if (!go->dvd_mode) + for (i = 0; i < 4; ++i) { + if (go->modet[i].enable) { + go->modet_enable = 1; + continue; + } + go->modet[i].pixel_threshold = 32767; + go->modet[i].motion_threshold = 32767; + go->modet[i].mb_threshold = 32767; + } + + if (go7007_construct_fw_image(go, &fw, &fw_len) < 0) + return -1; + + if (go7007_send_firmware(go, fw, fw_len) < 0 || + go7007_read_interrupt(go, &intr_val, &intr_data) < 0) { + v4l2_err(&go->v4l2_dev, "error transferring firmware\n"); + rv = -1; + goto start_error; + } + + go->state = STATE_DATA; + go->parse_length = 0; + go->seen_frame = 0; + if (go7007_stream_start(go) < 0) { + v4l2_err(&go->v4l2_dev, "error starting stream transfer\n"); + rv = -1; + goto start_error; + } + +start_error: + kfree(fw); + return rv; +} + +/* + * Store a byte in the current video buffer, if there is one. + */ +static inline void store_byte(struct go7007_buffer *gobuf, u8 byte) +{ + if (gobuf != NULL && gobuf->bytesused < GO7007_BUF_SIZE) { + unsigned int pgidx = gobuf->offset >> PAGE_SHIFT; + unsigned int pgoff = gobuf->offset & ~PAGE_MASK; + + *((u8 *)page_address(gobuf->pages[pgidx]) + pgoff) = byte; + ++gobuf->offset; + ++gobuf->bytesused; + } +} + +/* + * Deliver the last video buffer and get a new one to start writing to. + */ +static void frame_boundary(struct go7007 *go) +{ + struct go7007_buffer *gobuf; + int i; + + if (go->active_buf) { + if (go->active_buf->modet_active) { + if (go->active_buf->bytesused + 216 < GO7007_BUF_SIZE) { + for (i = 0; i < 216; ++i) + store_byte(go->active_buf, + go->active_map[i]); + go->active_buf->bytesused -= 216; + } else + go->active_buf->modet_active = 0; + } + go->active_buf->state = BUF_STATE_DONE; + wake_up_interruptible(&go->frame_waitq); + go->active_buf = NULL; + } + list_for_each_entry(gobuf, &go->stream, stream) + if (gobuf->state == BUF_STATE_QUEUED) { + gobuf->seq = go->next_seq; + do_gettimeofday(&gobuf->timestamp); + go->active_buf = gobuf; + break; + } + ++go->next_seq; +} + +static void write_bitmap_word(struct go7007 *go) +{ + int x, y, i, stride = ((go->width >> 4) + 7) >> 3; + + for (i = 0; i < 16; ++i) { + y = (((go->parse_length - 1) << 3) + i) / (go->width >> 4); + x = (((go->parse_length - 1) << 3) + i) % (go->width >> 4); + if (stride * y + (x >> 3) < sizeof(go->active_map)) + go->active_map[stride * y + (x >> 3)] |= + (go->modet_word & 1) << (x & 0x7); + go->modet_word >>= 1; + } +} + +/* + * Parse a chunk of the video stream into frames. The frames are not + * delimited by the hardware, so we have to parse the frame boundaries + * based on the type of video stream we're receiving. + */ +void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length) +{ + int i, seq_start_code = -1, frame_start_code = -1; + + spin_lock(&go->spinlock); + + switch (go->format) { + case GO7007_FORMAT_MPEG4: + seq_start_code = 0xB0; + frame_start_code = 0xB6; + break; + case GO7007_FORMAT_MPEG1: + case GO7007_FORMAT_MPEG2: + seq_start_code = 0xB3; + frame_start_code = 0x00; + break; + } + + for (i = 0; i < length; ++i) { + if (go->active_buf != NULL && + go->active_buf->bytesused >= GO7007_BUF_SIZE - 3) { + v4l2_info(&go->v4l2_dev, "dropping oversized frame\n"); + go->active_buf->offset -= go->active_buf->bytesused; + go->active_buf->bytesused = 0; + go->active_buf->modet_active = 0; + go->active_buf = NULL; + } + + switch (go->state) { + case STATE_DATA: + switch (buf[i]) { + case 0x00: + go->state = STATE_00; + break; + case 0xFF: + go->state = STATE_FF; + break; + default: + store_byte(go->active_buf, buf[i]); + break; + } + break; + case STATE_00: + switch (buf[i]) { + case 0x00: + go->state = STATE_00_00; + break; + case 0xFF: + store_byte(go->active_buf, 0x00); + go->state = STATE_FF; + break; + default: + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_00_00: + switch (buf[i]) { + case 0x00: + store_byte(go->active_buf, 0x00); + /* go->state remains STATE_00_00 */ + break; + case 0x01: + go->state = STATE_00_00_01; + break; + case 0xFF: + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x00); + go->state = STATE_FF; + break; + default: + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_00_00_01: + if (buf[i] == 0xF8 && go->modet_enable == 0) { + /* MODET start code, but MODET not enabled */ + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x01); + store_byte(go->active_buf, 0xF8); + go->state = STATE_DATA; + break; + } + /* If this is the start of a new MPEG frame, + * get a new buffer */ + if ((go->format == GO7007_FORMAT_MPEG1 || + go->format == GO7007_FORMAT_MPEG2 || + go->format == GO7007_FORMAT_MPEG4) && + (buf[i] == seq_start_code || + buf[i] == 0xB8 || /* GOP code */ + buf[i] == frame_start_code)) { + if (go->active_buf == NULL || go->seen_frame) + frame_boundary(go); + if (buf[i] == frame_start_code) { + if (go->active_buf != NULL) + go->active_buf->frame_offset = + go->active_buf->offset; + go->seen_frame = 1; + } else { + go->seen_frame = 0; + } + } + /* Handle any special chunk types, or just write the + * start code to the (potentially new) buffer */ + switch (buf[i]) { + case 0xF5: /* timestamp */ + go->parse_length = 12; + go->state = STATE_UNPARSED; + break; + case 0xF6: /* vbi */ + go->state = STATE_VBI_LEN_A; + break; + case 0xF8: /* MD map */ + go->parse_length = 0; + memset(go->active_map, 0, + sizeof(go->active_map)); + go->state = STATE_MODET_MAP; + break; + case 0xFF: /* Potential JPEG start code */ + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x01); + go->state = STATE_FF; + break; + default: + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x00); + store_byte(go->active_buf, 0x01); + store_byte(go->active_buf, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_FF: + switch (buf[i]) { + case 0x00: + store_byte(go->active_buf, 0xFF); + go->state = STATE_00; + break; + case 0xFF: + store_byte(go->active_buf, 0xFF); + /* go->state remains STATE_FF */ + break; + case 0xD8: + if (go->format == GO7007_FORMAT_MJPEG) + frame_boundary(go); + /* fall through */ + default: + store_byte(go->active_buf, 0xFF); + store_byte(go->active_buf, buf[i]); + go->state = STATE_DATA; + break; + } + break; + case STATE_VBI_LEN_A: + go->parse_length = buf[i] << 8; + go->state = STATE_VBI_LEN_B; + break; + case STATE_VBI_LEN_B: + go->parse_length |= buf[i]; + if (go->parse_length > 0) + go->state = STATE_UNPARSED; + else + go->state = STATE_DATA; + break; + case STATE_MODET_MAP: + if (go->parse_length < 204) { + if (go->parse_length & 1) { + go->modet_word |= buf[i]; + write_bitmap_word(go); + } else + go->modet_word = buf[i] << 8; + } else if (go->parse_length == 207 && go->active_buf) { + go->active_buf->modet_active = buf[i]; + } + if (++go->parse_length == 208) + go->state = STATE_DATA; + break; + case STATE_UNPARSED: + if (--go->parse_length == 0) + go->state = STATE_DATA; + break; + } + } + + spin_unlock(&go->spinlock); +} +EXPORT_SYMBOL(go7007_parse_video_stream); + +/* + * Allocate a new go7007 struct. Used by the hardware-specific probe. + */ +struct go7007 *go7007_alloc(struct go7007_board_info *board, struct device *dev) +{ + struct go7007 *go; + int i; + + go = kmalloc(sizeof(struct go7007), GFP_KERNEL); + if (go == NULL) + return NULL; + go->dev = dev; + go->board_info = board; + go->board_id = 0; + go->tuner_type = -1; + go->channel_number = 0; + go->name[0] = 0; + mutex_init(&go->hw_lock); + init_waitqueue_head(&go->frame_waitq); + spin_lock_init(&go->spinlock); + go->video_dev = NULL; + go->ref_count = 0; + go->status = STATUS_INIT; + memset(&go->i2c_adapter, 0, sizeof(go->i2c_adapter)); + go->i2c_adapter_online = 0; + go->interrupt_available = 0; + init_waitqueue_head(&go->interrupt_waitq); + go->in_use = 0; + go->input = 0; + if (board->sensor_flags & GO7007_SENSOR_TV) { + go->standard = GO7007_STD_NTSC; + go->width = 720; + go->height = 480; + go->sensor_framerate = 30000; + } else { + go->standard = GO7007_STD_OTHER; + go->width = board->sensor_width; + go->height = board->sensor_height; + go->sensor_framerate = board->sensor_framerate; + } + go->encoder_v_offset = board->sensor_v_offset; + go->encoder_h_offset = board->sensor_h_offset; + go->encoder_h_halve = 0; + go->encoder_v_halve = 0; + go->encoder_subsample = 0; + go->streaming = 0; + go->format = GO7007_FORMAT_MJPEG; + go->bitrate = 1500000; + go->fps_scale = 1; + go->pali = 0; + go->aspect_ratio = GO7007_RATIO_1_1; + go->gop_size = 0; + go->ipb = 0; + go->closed_gop = 0; + go->repeat_seqhead = 0; + go->seq_header_enable = 0; + go->gop_header_enable = 0; + go->dvd_mode = 0; + go->interlace_coding = 0; + for (i = 0; i < 4; ++i) + go->modet[i].enable = 0; + for (i = 0; i < 1624; ++i) + go->modet_map[i] = 0; + go->audio_deliver = NULL; + go->audio_enabled = 0; + INIT_LIST_HEAD(&go->stream); + + return go; +} +EXPORT_SYMBOL(go7007_alloc); + +/* + * Detach and unregister the encoder. The go7007 struct won't be freed + * until v4l2 finishes releasing its resources and all associated fds are + * closed by applications. + */ +void go7007_remove(struct go7007 *go) +{ + if (go->i2c_adapter_online) { + if (i2c_del_adapter(&go->i2c_adapter) == 0) + go->i2c_adapter_online = 0; + else + v4l2_err(&go->v4l2_dev, + "error removing I2C adapter!\n"); + } + + if (go->audio_enabled) + go7007_snd_remove(go); + go7007_v4l2_remove(go); +} +EXPORT_SYMBOL(go7007_remove); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/go7007-fw.c b/drivers/staging/media/go7007/go7007-fw.c new file mode 100644 index 000000000000..c9a6409edfe3 --- /dev/null +++ b/drivers/staging/media/go7007/go7007-fw.c @@ -0,0 +1,1636 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +/* + * This file contains code to generate a firmware image for the GO7007SB + * encoder. Much of the firmware is read verbatim from a file, but some of + * it concerning bitrate control and other things that can be configured at + * run-time are generated dynamically. Note that the format headers + * generated here do not affect the functioning of the encoder; they are + * merely parroted back to the host at the start of each frame. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +/* Constants used in the source firmware image to describe code segments */ + +#define FLAG_MODE_MJPEG (1) +#define FLAG_MODE_MPEG1 (1<<1) +#define FLAG_MODE_MPEG2 (1<<2) +#define FLAG_MODE_MPEG4 (1<<3) +#define FLAG_MODE_H263 (1<<4) +#define FLAG_MODE_ALL (FLAG_MODE_MJPEG | FLAG_MODE_MPEG1 | \ + FLAG_MODE_MPEG2 | FLAG_MODE_MPEG4 | \ + FLAG_MODE_H263) +#define FLAG_SPECIAL (1<<8) + +#define SPECIAL_FRM_HEAD 0 +#define SPECIAL_BRC_CTRL 1 +#define SPECIAL_CONFIG 2 +#define SPECIAL_SEQHEAD 3 +#define SPECIAL_AV_SYNC 4 +#define SPECIAL_FINAL 5 +#define SPECIAL_AUDIO 6 +#define SPECIAL_MODET 7 + +/* Little data class for creating MPEG headers bit-by-bit */ + +struct code_gen { + unsigned char *p; /* destination */ + u32 a; /* collects bits at the top of the variable */ + int b; /* bit position of most recently-written bit */ + int len; /* written out so far */ +}; + +#define CODE_GEN(name, dest) struct code_gen name = { dest, 0, 32, 0 } + +#define CODE_ADD(name, val, length) do { \ + name.b -= (length); \ + name.a |= (val) << name.b; \ + while (name.b <= 24) { \ + *name.p = name.a >> 24; \ + ++name.p; \ + name.a <<= 8; \ + name.b += 8; \ + name.len += 8; \ + } \ +} while (0) + +#define CODE_LENGTH(name) (name.len + (32 - name.b)) + +/* Tables for creating the bitrate control data */ + +static const s16 converge_speed_ip[101] = { + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 5, 5, 5, 6, 6, 6, 7, 7, 8, 8, + 9, 10, 10, 11, 12, 13, 14, 15, 16, 17, + 19, 20, 22, 23, 25, 27, 30, 32, 35, 38, + 41, 45, 49, 53, 58, 63, 69, 76, 83, 91, + 100 +}; + +static const s16 converge_speed_ipb[101] = { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, + 6, 6, 6, 7, 7, 7, 7, 8, 8, 9, + 9, 9, 10, 10, 11, 12, 12, 13, 14, 14, + 15, 16, 17, 18, 19, 20, 22, 23, 25, 26, + 28, 30, 32, 34, 37, 40, 42, 46, 49, 53, + 57, 61, 66, 71, 77, 83, 90, 97, 106, 115, + 125, 135, 147, 161, 175, 191, 209, 228, 249, 273, + 300 +}; + +static const s16 LAMBDA_table[4][101] = { + { 16, 16, 16, 16, 17, 17, 17, 18, 18, 18, + 19, 19, 19, 20, 20, 20, 21, 21, 22, 22, + 22, 23, 23, 24, 24, 25, 25, 25, 26, 26, + 27, 27, 28, 28, 29, 29, 30, 31, 31, 32, + 32, 33, 33, 34, 35, 35, 36, 37, 37, 38, + 39, 39, 40, 41, 42, 42, 43, 44, 45, 46, + 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, + 67, 68, 69, 70, 72, 73, 74, 76, 77, 78, + 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, + 96 + }, + { + 20, 20, 20, 21, 21, 21, 22, 22, 23, 23, + 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, + 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, + 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, + 40, 41, 42, 43, 43, 44, 45, 46, 47, 48, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, + 70, 71, 72, 73, 75, 76, 78, 79, 80, 82, + 83, 85, 86, 88, 90, 91, 93, 95, 96, 98, + 100, 102, 103, 105, 107, 109, 111, 113, 115, 117, + 120 + }, + { + 24, 24, 24, 25, 25, 26, 26, 27, 27, 28, + 28, 29, 29, 30, 30, 31, 31, 32, 33, 33, + 34, 34, 35, 36, 36, 37, 38, 38, 39, 40, + 41, 41, 42, 43, 44, 44, 45, 46, 47, 48, + 49, 50, 50, 51, 52, 53, 54, 55, 56, 57, + 58, 59, 60, 62, 63, 64, 65, 66, 67, 69, + 70, 71, 72, 74, 75, 76, 78, 79, 81, 82, + 84, 85, 87, 88, 90, 92, 93, 95, 97, 98, + 100, 102, 104, 106, 108, 110, 112, 114, 116, 118, + 120, 122, 124, 127, 129, 131, 134, 136, 138, 141, + 144 + }, + { + 32, 32, 33, 33, 34, 34, 35, 36, 36, 37, + 38, 38, 39, 40, 41, 41, 42, 43, 44, 44, + 45, 46, 47, 48, 49, 50, 50, 51, 52, 53, + 54, 55, 56, 57, 58, 59, 60, 62, 63, 64, + 65, 66, 67, 69, 70, 71, 72, 74, 75, 76, + 78, 79, 81, 82, 84, 85, 87, 88, 90, 92, + 93, 95, 97, 98, 100, 102, 104, 106, 108, 110, + 112, 114, 116, 118, 120, 122, 124, 127, 129, 131, + 134, 136, 139, 141, 144, 146, 149, 152, 154, 157, + 160, 163, 166, 169, 172, 175, 178, 181, 185, 188, + 192 + } +}; + +/* MPEG blank frame generation tables */ + +enum mpeg_frame_type { + PFRAME, + BFRAME_PRE, + BFRAME_POST, + BFRAME_BIDIR, + BFRAME_EMPTY +}; + +static const u32 addrinctab[33][2] = { + { 0x01, 1 }, { 0x03, 3 }, { 0x02, 3 }, { 0x03, 4 }, + { 0x02, 4 }, { 0x03, 5 }, { 0x02, 5 }, { 0x07, 7 }, + { 0x06, 7 }, { 0x0b, 8 }, { 0x0a, 8 }, { 0x09, 8 }, + { 0x08, 8 }, { 0x07, 8 }, { 0x06, 8 }, { 0x17, 10 }, + { 0x16, 10 }, { 0x15, 10 }, { 0x14, 10 }, { 0x13, 10 }, + { 0x12, 10 }, { 0x23, 11 }, { 0x22, 11 }, { 0x21, 11 }, + { 0x20, 11 }, { 0x1f, 11 }, { 0x1e, 11 }, { 0x1d, 11 }, + { 0x1c, 11 }, { 0x1b, 11 }, { 0x1a, 11 }, { 0x19, 11 }, + { 0x18, 11 } +}; + +/* Standard JPEG tables */ + +static const u8 default_intra_quant_table[] = { + 8, 16, 19, 22, 26, 27, 29, 34, + 16, 16, 22, 24, 27, 29, 34, 37, + 19, 22, 26, 27, 29, 34, 34, 38, + 22, 22, 26, 27, 29, 34, 37, 40, + 22, 26, 27, 29, 32, 35, 40, 48, + 26, 27, 29, 32, 35, 40, 48, 58, + 26, 27, 29, 34, 38, 46, 56, 69, + 27, 29, 35, 38, 46, 56, 69, 83 +}; + +static const u8 bits_dc_luminance[] = { + 0, 0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0 +}; + +static const u8 val_dc_luminance[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const u8 bits_dc_chrominance[] = { + 0, 0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0 +}; + +static const u8 val_dc_chrominance[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 +}; + +static const u8 bits_ac_luminance[] = { + 0, 0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 0x7d +}; + +static const u8 val_ac_luminance[] = { + 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 +}; + +static const u8 bits_ac_chrominance[] = { + 0, 0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 0x77 +}; + +static const u8 val_ac_chrominance[] = { + 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 +}; + +/* Zig-zag mapping for quant table + * + * OK, let's do this mapping on the actual table above so it doesn't have + * to be done on the fly. + */ +static const int zz[64] = { + 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, + 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, + 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23, 30, 37, 44, 51, + 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63 +}; + +static int copy_packages(__le16 *dest, u16 *src, int pkg_cnt, int space) +{ + int i, cnt = pkg_cnt * 32; + + if (space < cnt) + return -1; + + for (i = 0; i < cnt; ++i) + dest[i] = cpu_to_le16p(src + i); + + return cnt; +} + +static int mjpeg_frame_header(struct go7007 *go, unsigned char *buf, int q) +{ + int i, p = 0; + + buf[p++] = 0xff; + buf[p++] = 0xd8; + buf[p++] = 0xff; + buf[p++] = 0xdb; + buf[p++] = 0; + buf[p++] = 2 + 65; + buf[p++] = 0; + buf[p++] = default_intra_quant_table[0]; + for (i = 1; i < 64; ++i) + /* buf[p++] = (default_intra_quant_table[i] * q) >> 3; */ + buf[p++] = (default_intra_quant_table[zz[i]] * q) >> 3; + buf[p++] = 0xff; + buf[p++] = 0xc0; + buf[p++] = 0; + buf[p++] = 17; + buf[p++] = 8; + buf[p++] = go->height >> 8; + buf[p++] = go->height & 0xff; + buf[p++] = go->width >> 8; + buf[p++] = go->width & 0xff; + buf[p++] = 3; + buf[p++] = 1; + buf[p++] = 0x22; + buf[p++] = 0; + buf[p++] = 2; + buf[p++] = 0x11; + buf[p++] = 0; + buf[p++] = 3; + buf[p++] = 0x11; + buf[p++] = 0; + buf[p++] = 0xff; + buf[p++] = 0xc4; + buf[p++] = 418 >> 8; + buf[p++] = 418 & 0xff; + buf[p++] = 0x00; + memcpy(buf + p, bits_dc_luminance + 1, 16); + p += 16; + memcpy(buf + p, val_dc_luminance, sizeof(val_dc_luminance)); + p += sizeof(val_dc_luminance); + buf[p++] = 0x01; + memcpy(buf + p, bits_dc_chrominance + 1, 16); + p += 16; + memcpy(buf + p, val_dc_chrominance, sizeof(val_dc_chrominance)); + p += sizeof(val_dc_chrominance); + buf[p++] = 0x10; + memcpy(buf + p, bits_ac_luminance + 1, 16); + p += 16; + memcpy(buf + p, val_ac_luminance, sizeof(val_ac_luminance)); + p += sizeof(val_ac_luminance); + buf[p++] = 0x11; + memcpy(buf + p, bits_ac_chrominance + 1, 16); + p += 16; + memcpy(buf + p, val_ac_chrominance, sizeof(val_ac_chrominance)); + p += sizeof(val_ac_chrominance); + buf[p++] = 0xff; + buf[p++] = 0xda; + buf[p++] = 0; + buf[p++] = 12; + buf[p++] = 3; + buf[p++] = 1; + buf[p++] = 0x00; + buf[p++] = 2; + buf[p++] = 0x11; + buf[p++] = 3; + buf[p++] = 0x11; + buf[p++] = 0; + buf[p++] = 63; + buf[p++] = 0; + return p; +} + +static int gen_mjpeghdr_to_package(struct go7007 *go, __le16 *code, int space) +{ + u8 *buf; + u16 mem = 0x3e00; + unsigned int addr = 0x19; + int size = 0, i, off = 0, chunk; + + buf = kzalloc(4096, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "go7007: unable to allocate 4096 bytes for " + "firmware construction\n"); + return -1; + } + + for (i = 1; i < 32; ++i) { + mjpeg_frame_header(go, buf + size, i); + size += 80; + } + chunk = mjpeg_frame_header(go, buf + size, 1); + memmove(buf + size, buf + size + 80, chunk - 80); + size += chunk - 80; + + for (i = 0; i < size; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > size) + chunk = (size - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr++); + mem = 0x3e00; + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } +done: + kfree(buf); + return off; +} + +static int mpeg1_frame_header(struct go7007 *go, unsigned char *buf, + int modulo, int pict_struct, enum mpeg_frame_type frame) +{ + int i, j, mb_code, mb_len; + int rows = go->interlace_coding ? go->height / 32 : go->height / 16; + CODE_GEN(c, buf + 6); + + switch (frame) { + case PFRAME: + mb_code = 0x1; + mb_len = 3; + break; + case BFRAME_PRE: + mb_code = 0x2; + mb_len = 4; + break; + case BFRAME_POST: + mb_code = 0x2; + mb_len = 3; + break; + case BFRAME_BIDIR: + mb_code = 0x2; + mb_len = 2; + break; + default: /* keep the compiler happy */ + mb_code = mb_len = 0; + break; + } + + CODE_ADD(c, frame == PFRAME ? 0x2 : 0x3, 13); + CODE_ADD(c, 0xffff, 16); + CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4); + if (frame != PFRAME) + CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 0x7 : 0x4, 4); + else + CODE_ADD(c, 0, 4); /* Is this supposed to be here?? */ + CODE_ADD(c, 0, 3); /* What is this?? */ + /* Byte-align with zeros */ + j = 8 - (CODE_LENGTH(c) % 8); + if (j != 8) + CODE_ADD(c, 0, j); + + if (go->format == GO7007_FORMAT_MPEG2) { + CODE_ADD(c, 0x1, 24); + CODE_ADD(c, 0xb5, 8); + CODE_ADD(c, 0x844, 12); + CODE_ADD(c, frame == PFRAME ? 0xff : 0x44, 8); + if (go->interlace_coding) { + CODE_ADD(c, pict_struct, 4); + if (go->dvd_mode) + CODE_ADD(c, 0x000, 11); + else + CODE_ADD(c, 0x200, 11); + } else { + CODE_ADD(c, 0x3, 4); + CODE_ADD(c, 0x20c, 11); + } + /* Byte-align with zeros */ + j = 8 - (CODE_LENGTH(c) % 8); + if (j != 8) + CODE_ADD(c, 0, j); + } + + for (i = 0; i < rows; ++i) { + CODE_ADD(c, 1, 24); + CODE_ADD(c, i + 1, 8); + CODE_ADD(c, 0x2, 6); + CODE_ADD(c, 0x1, 1); + CODE_ADD(c, mb_code, mb_len); + if (go->interlace_coding) { + CODE_ADD(c, 0x1, 2); + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + if (frame == BFRAME_BIDIR) { + CODE_ADD(c, 0x3, 2); + if (go->interlace_coding) + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + CODE_ADD(c, 0x3, 2); + for (j = (go->width >> 4) - 2; j >= 33; j -= 33) + CODE_ADD(c, 0x8, 11); + CODE_ADD(c, addrinctab[j][0], addrinctab[j][1]); + CODE_ADD(c, mb_code, mb_len); + if (go->interlace_coding) { + CODE_ADD(c, 0x1, 2); + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + if (frame == BFRAME_BIDIR) { + CODE_ADD(c, 0x3, 2); + if (go->interlace_coding) + CODE_ADD(c, pict_struct == 1 ? 0x0 : 0x1, 1); + } + CODE_ADD(c, 0x3, 2); + + /* Byte-align with zeros */ + j = 8 - (CODE_LENGTH(c) % 8); + if (j != 8) + CODE_ADD(c, 0, j); + } + + i = CODE_LENGTH(c) + 4 * 8; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x01; + buf[5] = 0x00; + return i; +} + +static int mpeg1_sequence_header(struct go7007 *go, unsigned char *buf, int ext) +{ + int i, aspect_ratio, picture_rate; + CODE_GEN(c, buf + 6); + + if (go->format == GO7007_FORMAT_MPEG1) { + switch (go->aspect_ratio) { + case GO7007_RATIO_4_3: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; + break; + case GO7007_RATIO_16_9: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; + break; + default: + aspect_ratio = 1; + break; + } + } else { + switch (go->aspect_ratio) { + case GO7007_RATIO_4_3: + aspect_ratio = 2; + break; + case GO7007_RATIO_16_9: + aspect_ratio = 3; + break; + default: + aspect_ratio = 1; + break; + } + } + switch (go->sensor_framerate) { + case 24000: + picture_rate = 1; + break; + case 24024: + picture_rate = 2; + break; + case 25025: + picture_rate = go->interlace_coding ? 6 : 3; + break; + case 30000: + picture_rate = go->interlace_coding ? 7 : 4; + break; + case 30030: + picture_rate = go->interlace_coding ? 8 : 5; + break; + default: + picture_rate = 5; /* 30 fps seems like a reasonable default */ + break; + } + + CODE_ADD(c, go->width, 12); + CODE_ADD(c, go->height, 12); + CODE_ADD(c, aspect_ratio, 4); + CODE_ADD(c, picture_rate, 4); + CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 20000 : 0x3ffff, 18); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->format == GO7007_FORMAT_MPEG2 ? 112 : 20, 10); + CODE_ADD(c, 0, 3); + + /* Byte-align with zeros */ + i = 8 - (CODE_LENGTH(c) % 8); + if (i != 8) + CODE_ADD(c, 0, i); + + if (go->format == GO7007_FORMAT_MPEG2) { + CODE_ADD(c, 0x1, 24); + CODE_ADD(c, 0xb5, 8); + CODE_ADD(c, 0x148, 12); + if (go->interlace_coding) + CODE_ADD(c, 0x20001, 20); + else + CODE_ADD(c, 0xa0001, 20); + CODE_ADD(c, 0, 16); + + /* Byte-align with zeros */ + i = 8 - (CODE_LENGTH(c) % 8); + if (i != 8) + CODE_ADD(c, 0, i); + + if (ext) { + CODE_ADD(c, 0x1, 24); + CODE_ADD(c, 0xb52, 12); + CODE_ADD(c, go->standard == GO7007_STD_NTSC ? 2 : 1, 3); + CODE_ADD(c, 0x105, 9); + CODE_ADD(c, 0x505, 16); + CODE_ADD(c, go->width, 14); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->height, 14); + + /* Byte-align with zeros */ + i = 8 - (CODE_LENGTH(c) % 8); + if (i != 8) + CODE_ADD(c, 0, i); + } + } + + i = CODE_LENGTH(c) + 4 * 8; + buf[0] = i & 0xff; + buf[1] = i >> 8; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x01; + buf[5] = 0xb3; + return i; +} + +static int gen_mpeg1hdr_to_package(struct go7007 *go, + __le16 *code, int space, int *framelen) +{ + u8 *buf; + u16 mem = 0x3e00; + unsigned int addr = 0x19; + int i, off = 0, chunk; + + buf = kzalloc(5120, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "go7007: unable to allocate 5120 bytes for " + "firmware construction\n"); + return -1; + } + framelen[0] = mpeg1_frame_header(go, buf, 0, 1, PFRAME); + if (go->interlace_coding) + framelen[0] += mpeg1_frame_header(go, buf + framelen[0] / 8, + 0, 2, PFRAME); + buf[0] = framelen[0] & 0xff; + buf[1] = framelen[0] >> 8; + i = 368; + framelen[1] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_PRE); + if (go->interlace_coding) + framelen[1] += mpeg1_frame_header(go, buf + i + framelen[1] / 8, + 0, 2, BFRAME_PRE); + buf[i] = framelen[1] & 0xff; + buf[i + 1] = framelen[1] >> 8; + i += 1632; + framelen[2] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_POST); + if (go->interlace_coding) + framelen[2] += mpeg1_frame_header(go, buf + i + framelen[2] / 8, + 0, 2, BFRAME_POST); + buf[i] = framelen[2] & 0xff; + buf[i + 1] = framelen[2] >> 8; + i += 1432; + framelen[3] = mpeg1_frame_header(go, buf + i, 0, 1, BFRAME_BIDIR); + if (go->interlace_coding) + framelen[3] += mpeg1_frame_header(go, buf + i + framelen[3] / 8, + 0, 2, BFRAME_BIDIR); + buf[i] = framelen[3] & 0xff; + buf[i + 1] = framelen[3] >> 8; + i += 1632 + 16; + mpeg1_sequence_header(go, buf + i, 0); + i += 40; + for (i = 0; i < 5120; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > 5120) + chunk = (5120 - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr); + if (mem + chunk == 0x4000) { + mem = 0x3e00; + ++addr; + } + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } +done: + kfree(buf); + return off; +} + +static int vti_bitlen(struct go7007 *go) +{ + unsigned int i, max_time_incr = go->sensor_framerate / go->fps_scale; + + for (i = 31; (max_time_incr & ((1 << i) - 1)) == max_time_incr; --i); + return i + 1; +} + +static int mpeg4_frame_header(struct go7007 *go, unsigned char *buf, + int modulo, enum mpeg_frame_type frame) +{ + int i; + CODE_GEN(c, buf + 6); + int mb_count = (go->width >> 4) * (go->height >> 4); + + CODE_ADD(c, frame == PFRAME ? 0x1 : 0x2, 2); + if (modulo) + CODE_ADD(c, 0x1, 1); + CODE_ADD(c, 0x1, 2); + CODE_ADD(c, 0, vti_bitlen(go)); + CODE_ADD(c, 0x3, 2); + if (frame == PFRAME) + CODE_ADD(c, 0, 1); + CODE_ADD(c, 0xc, 11); + if (frame != PFRAME) + CODE_ADD(c, 0x4, 3); + if (frame != BFRAME_EMPTY) { + for (i = 0; i < mb_count; ++i) { + switch (frame) { + case PFRAME: + CODE_ADD(c, 0x1, 1); + break; + case BFRAME_PRE: + CODE_ADD(c, 0x47, 8); + break; + case BFRAME_POST: + CODE_ADD(c, 0x27, 7); + break; + case BFRAME_BIDIR: + CODE_ADD(c, 0x5f, 8); + break; + case BFRAME_EMPTY: /* keep compiler quiet */ + break; + } + } + } + + /* Byte-align with a zero followed by ones */ + i = 8 - (CODE_LENGTH(c) % 8); + CODE_ADD(c, 0, 1); + CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); + + i = CODE_LENGTH(c) + 4 * 8; + buf[0] = i & 0xff; + buf[1] = i >> 8; + buf[2] = 0x00; + buf[3] = 0x00; + buf[4] = 0x01; + buf[5] = 0xb6; + return i; +} + +static int mpeg4_sequence_header(struct go7007 *go, unsigned char *buf, int ext) +{ + const unsigned char head[] = { 0x00, 0x00, 0x01, 0xb0, go->pali, + 0x00, 0x00, 0x01, 0xb5, 0x09, + 0x00, 0x00, 0x01, 0x00, + 0x00, 0x00, 0x01, 0x20, }; + int i, aspect_ratio; + int fps = go->sensor_framerate / go->fps_scale; + CODE_GEN(c, buf + 2 + sizeof(head)); + + switch (go->aspect_ratio) { + case GO7007_RATIO_4_3: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 3 : 2; + break; + case GO7007_RATIO_16_9: + aspect_ratio = go->standard == GO7007_STD_NTSC ? 5 : 4; + break; + default: + aspect_ratio = 1; + break; + } + + memcpy(buf + 2, head, sizeof(head)); + CODE_ADD(c, 0x191, 17); + CODE_ADD(c, aspect_ratio, 4); + CODE_ADD(c, 0x1, 4); + CODE_ADD(c, fps, 16); + CODE_ADD(c, 0x3, 2); + CODE_ADD(c, 1001, vti_bitlen(go)); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->width, 13); + CODE_ADD(c, 1, 1); + CODE_ADD(c, go->height, 13); + CODE_ADD(c, 0x2830, 14); + + /* Byte-align */ + i = 8 - (CODE_LENGTH(c) % 8); + CODE_ADD(c, 0, 1); + CODE_ADD(c, (1 << (i - 1)) - 1, i - 1); + + i = CODE_LENGTH(c) + sizeof(head) * 8; + buf[0] = i & 0xff; + buf[1] = i >> 8; + return i; +} + +static int gen_mpeg4hdr_to_package(struct go7007 *go, + __le16 *code, int space, int *framelen) +{ + u8 *buf; + u16 mem = 0x3e00; + unsigned int addr = 0x19; + int i, off = 0, chunk; + + buf = kzalloc(5120, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "go7007: unable to allocate 5120 bytes for " + "firmware construction\n"); + return -1; + } + framelen[0] = mpeg4_frame_header(go, buf, 0, PFRAME); + i = 368; + framelen[1] = mpeg4_frame_header(go, buf + i, 0, BFRAME_PRE); + i += 1632; + framelen[2] = mpeg4_frame_header(go, buf + i, 0, BFRAME_POST); + i += 1432; + framelen[3] = mpeg4_frame_header(go, buf + i, 0, BFRAME_BIDIR); + i += 1632; + mpeg4_frame_header(go, buf + i, 0, BFRAME_EMPTY); + i += 16; + mpeg4_sequence_header(go, buf + i, 0); + i += 40; + for (i = 0; i < 5120; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > 5120) + chunk = (5120 - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr); + if (mem + chunk == 0x4000) { + mem = 0x3e00; + ++addr; + } + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } + mem = 0x3e00; + addr = go->ipb ? 0x14f9 : 0x0af9; + memset(buf, 0, 5120); + framelen[4] = mpeg4_frame_header(go, buf, 1, PFRAME); + i = 368; + framelen[5] = mpeg4_frame_header(go, buf + i, 1, BFRAME_PRE); + i += 1632; + framelen[6] = mpeg4_frame_header(go, buf + i, 1, BFRAME_POST); + i += 1432; + framelen[7] = mpeg4_frame_header(go, buf + i, 1, BFRAME_BIDIR); + i += 1632; + mpeg4_frame_header(go, buf + i, 1, BFRAME_EMPTY); + i += 16; + for (i = 0; i < 5120; i += chunk * 2) { + if (space - off < 32) { + off = -1; + goto done; + } + + code[off + 1] = __cpu_to_le16(0x8000 | mem); + + chunk = 28; + if (mem + chunk > 0x4000) + chunk = 0x4000 - mem; + if (i + 2 * chunk > 5120) + chunk = (5120 - i) / 2; + + if (chunk < 28) { + code[off] = __cpu_to_le16(0x4000 | chunk); + code[off + 31] = __cpu_to_le16(addr); + if (mem + chunk == 0x4000) { + mem = 0x3e00; + ++addr; + } + } else { + code[off] = __cpu_to_le16(0x1000 | 28); + code[off + 31] = 0; + mem += 28; + } + + memcpy(&code[off + 2], buf + i, chunk * 2); + off += 32; + } +done: + kfree(buf); + return off; +} + +static int brctrl_to_package(struct go7007 *go, + __le16 *code, int space, int *framelen) +{ + int converge_speed = 0; + int lambda = (go->format == GO7007_FORMAT_MJPEG || go->dvd_mode) ? + 100 : 0; + int peak_rate = 6 * go->bitrate / 5; + int vbv_buffer = go->format == GO7007_FORMAT_MJPEG ? + go->bitrate : + (go->dvd_mode ? 900000 : peak_rate); + int fps = go->sensor_framerate / go->fps_scale; + int q = 0; + /* Bizarre math below depends on rounding errors in division */ + u32 sgop_expt_addr = go->bitrate / 32 * (go->ipb ? 3 : 1) * 1001 / fps; + u32 sgop_peak_addr = peak_rate / 32 * 1001 / fps; + u32 total_expt_addr = go->bitrate / 32 * 1000 / fps * (fps / 1000); + u32 vbv_alert_addr = vbv_buffer * 3 / (4 * 32); + u32 cplx[] = { + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + q > 0 ? sgop_expt_addr * q : + 2 * go->width * go->height * (go->ipb ? 6 : 4) / 32, + }; + u32 calc_q = q > 0 ? q : cplx[0] / sgop_expt_addr; + u16 pack[] = { + 0x200e, 0x0000, + 0xBF20, go->ipb ? converge_speed_ipb[converge_speed] + : converge_speed_ip[converge_speed], + 0xBF21, go->ipb ? 2 : 0, + 0xBF22, go->ipb ? LAMBDA_table[0][lambda / 2 + 50] + : 32767, + 0xBF23, go->ipb ? LAMBDA_table[1][lambda] : 32767, + 0xBF24, 32767, + 0xBF25, lambda > 99 ? 32767 : LAMBDA_table[3][lambda], + 0xBF26, sgop_expt_addr & 0x0000FFFF, + 0xBF27, sgop_expt_addr >> 16, + 0xBF28, sgop_peak_addr & 0x0000FFFF, + 0xBF29, sgop_peak_addr >> 16, + 0xBF2A, vbv_alert_addr & 0x0000FFFF, + 0xBF2B, vbv_alert_addr >> 16, + 0xBF2C, 0, + 0xBF2D, 0, + 0, 0, + + 0x200e, 0x0000, + 0xBF2E, vbv_alert_addr & 0x0000FFFF, + 0xBF2F, vbv_alert_addr >> 16, + 0xBF30, cplx[0] & 0x0000FFFF, + 0xBF31, cplx[0] >> 16, + 0xBF32, cplx[1] & 0x0000FFFF, + 0xBF33, cplx[1] >> 16, + 0xBF34, cplx[2] & 0x0000FFFF, + 0xBF35, cplx[2] >> 16, + 0xBF36, cplx[3] & 0x0000FFFF, + 0xBF37, cplx[3] >> 16, + 0xBF38, 0, + 0xBF39, 0, + 0xBF3A, total_expt_addr & 0x0000FFFF, + 0xBF3B, total_expt_addr >> 16, + 0, 0, + + 0x200e, 0x0000, + 0xBF3C, total_expt_addr & 0x0000FFFF, + 0xBF3D, total_expt_addr >> 16, + 0xBF3E, 0, + 0xBF3F, 0, + 0xBF48, 0, + 0xBF49, 0, + 0xBF4A, calc_q < 4 ? 4 : (calc_q > 124 ? 124 : calc_q), + 0xBF4B, 4, + 0xBF4C, 0, + 0xBF4D, 0, + 0xBF4E, 0, + 0xBF4F, 0, + 0xBF50, 0, + 0xBF51, 0, + 0, 0, + + 0x200e, 0x0000, + 0xBF40, sgop_expt_addr & 0x0000FFFF, + 0xBF41, sgop_expt_addr >> 16, + 0xBF42, 0, + 0xBF43, 0, + 0xBF44, 0, + 0xBF45, 0, + 0xBF46, (go->width >> 4) * (go->height >> 4), + 0xBF47, 0, + 0xBF64, 0, + 0xBF65, 0, + 0xBF18, framelen[4], + 0xBF19, framelen[5], + 0xBF1A, framelen[6], + 0xBF1B, framelen[7], + 0, 0, + +#if 0 + /* Remove once we don't care about matching */ + 0x200e, 0x0000, + 0xBF56, 4, + 0xBF57, 0, + 0xBF58, 5, + 0xBF59, 0, + 0xBF5A, 6, + 0xBF5B, 0, + 0xBF5C, 8, + 0xBF5D, 0, + 0xBF5E, 1, + 0xBF5F, 0, + 0xBF60, 1, + 0xBF61, 0, + 0xBF62, 0, + 0xBF63, 0, + 0, 0, +#else + 0x2008, 0x0000, + 0xBF56, 4, + 0xBF57, 0, + 0xBF58, 5, + 0xBF59, 0, + 0xBF5A, 6, + 0xBF5B, 0, + 0xBF5C, 8, + 0xBF5D, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, +#endif + + 0x200e, 0x0000, + 0xBF10, 0, + 0xBF11, 0, + 0xBF12, 0, + 0xBF13, 0, + 0xBF14, 0, + 0xBF15, 0, + 0xBF16, 0, + 0xBF17, 0, + 0xBF7E, 0, + 0xBF7F, 1, + 0xBF52, framelen[0], + 0xBF53, framelen[1], + 0xBF54, framelen[2], + 0xBF55, framelen[3], + 0, 0, + }; + + return copy_packages(code, pack, 6, space); +} + +static int config_package(struct go7007 *go, __le16 *code, int space) +{ + int fps = go->sensor_framerate / go->fps_scale / 1000; + int rows = go->interlace_coding ? go->height / 32 : go->height / 16; + int brc_window_size = fps; + int q_min = 2, q_max = 31; + int THACCoeffSet0 = 0; + u16 pack[] = { + 0x200e, 0x0000, + 0xc002, 0x14b4, + 0xc003, 0x28b4, + 0xc004, 0x3c5a, + 0xdc05, 0x2a77, + 0xc6c3, go->format == GO7007_FORMAT_MPEG4 ? 0 : + (go->format == GO7007_FORMAT_H263 ? 0 : 1), + 0xc680, go->format == GO7007_FORMAT_MPEG4 ? 0xf1 : + (go->format == GO7007_FORMAT_H263 ? 0x61 : + 0xd3), + 0xc780, 0x0140, + 0xe009, 0x0001, + 0xc60f, 0x0008, + 0xd4ff, 0x0002, + 0xe403, 2340, + 0xe406, 75, + 0xd411, 0x0001, + 0xd410, 0xa1d6, + 0x0001, 0x2801, + + 0x200d, 0x0000, + 0xe402, 0x018b, + 0xe401, 0x8b01, + 0xd472, (go->board_info->sensor_flags & + GO7007_SENSOR_TV) && + (!go->interlace_coding) ? + 0x01b0 : 0x0170, + 0xd475, (go->board_info->sensor_flags & + GO7007_SENSOR_TV) && + (!go->interlace_coding) ? + 0x0008 : 0x0009, + 0xc404, go->interlace_coding ? 0x44 : + (go->format == GO7007_FORMAT_MPEG4 ? 0x11 : + (go->format == GO7007_FORMAT_MPEG1 ? 0x02 : + (go->format == GO7007_FORMAT_MPEG2 ? 0x04 : + (go->format == GO7007_FORMAT_H263 ? 0x08 : + 0x20)))), + 0xbf0a, (go->format == GO7007_FORMAT_MPEG4 ? 8 : + (go->format == GO7007_FORMAT_MPEG1 ? 1 : + (go->format == GO7007_FORMAT_MPEG2 ? 2 : + (go->format == GO7007_FORMAT_H263 ? 4 : 16)))) | + ((go->repeat_seqhead ? 1 : 0) << 6) | + ((go->dvd_mode ? 1 : 0) << 9) | + ((go->gop_header_enable ? 1 : 0) << 10), + 0xbf0b, 0, + 0xdd5a, go->ipb ? 0x14 : 0x0a, + 0xbf0c, 0, + 0xbf0d, 0, + 0xc683, THACCoeffSet0, + 0xc40a, (go->width << 4) | rows, + 0xe01a, go->board_info->hpi_buffer_cap, + 0, 0, + 0, 0, + + 0x2008, 0, + 0xe402, 0x88, + 0xe401, 0x8f01, + 0xbf6a, 0, + 0xbf6b, 0, + 0xbf6c, 0, + 0xbf6d, 0, + 0xbf6e, 0, + 0xbf6f, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0x200e, 0, + 0xbf66, brc_window_size, + 0xbf67, 0, + 0xbf68, q_min, + 0xbf69, q_max, + 0xbfe0, 0, + 0xbfe1, 0, + 0xbfe2, 0, + 0xbfe3, go->ipb ? 3 : 1, + 0xc031, go->board_info->sensor_flags & + GO7007_SENSOR_VBI ? 1 : 0, + 0xc01c, 0x1f, + 0xdd8c, 0x15, + 0xdd94, 0x15, + 0xdd88, go->ipb ? 0x1401 : 0x0a01, + 0xdd90, go->ipb ? 0x1401 : 0x0a01, + 0, 0, + + 0x200e, 0, + 0xbfe4, 0, + 0xbfe5, 0, + 0xbfe6, 0, + 0xbfe7, fps << 8, + 0xbfe8, 0x3a00, + 0xbfe9, 0, + 0xbfea, 0, + 0xbfeb, 0, + 0xbfec, (go->interlace_coding ? 1 << 15 : 0) | + (go->modet_enable ? 0xa : 0) | + (go->board_info->sensor_flags & + GO7007_SENSOR_VBI ? 1 : 0), + 0xbfed, 0, + 0xbfee, 0, + 0xbfef, 0, + 0xbff0, go->board_info->sensor_flags & + GO7007_SENSOR_TV ? 0xf060 : 0xb060, + 0xbff1, 0, + 0, 0, + }; + + return copy_packages(code, pack, 5, space); +} + +static int seqhead_to_package(struct go7007 *go, __le16 *code, int space, + int (*sequence_header_func)(struct go7007 *go, + unsigned char *buf, int ext)) +{ + int vop_time_increment_bitlength = vti_bitlen(go); + int fps = go->sensor_framerate / go->fps_scale * + (go->interlace_coding ? 2 : 1); + unsigned char buf[40] = { }; + int len = sequence_header_func(go, buf, 1); + u16 pack[] = { + 0x2006, 0, + 0xbf08, fps, + 0xbf09, 0, + 0xbff2, vop_time_increment_bitlength, + 0xbff3, (1 << vop_time_increment_bitlength) - 1, + 0xbfe6, 0, + 0xbfe7, (fps / 1000) << 8, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0x2007, 0, + 0xc800, buf[2] << 8 | buf[3], + 0xc801, buf[4] << 8 | buf[5], + 0xc802, buf[6] << 8 | buf[7], + 0xc803, buf[8] << 8 | buf[9], + 0xc406, 64, + 0xc407, len - 64, + 0xc61b, 1, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + + 0x200e, 0, + 0xc808, buf[10] << 8 | buf[11], + 0xc809, buf[12] << 8 | buf[13], + 0xc80a, buf[14] << 8 | buf[15], + 0xc80b, buf[16] << 8 | buf[17], + 0xc80c, buf[18] << 8 | buf[19], + 0xc80d, buf[20] << 8 | buf[21], + 0xc80e, buf[22] << 8 | buf[23], + 0xc80f, buf[24] << 8 | buf[25], + 0xc810, buf[26] << 8 | buf[27], + 0xc811, buf[28] << 8 | buf[29], + 0xc812, buf[30] << 8 | buf[31], + 0xc813, buf[32] << 8 | buf[33], + 0xc814, buf[34] << 8 | buf[35], + 0xc815, buf[36] << 8 | buf[37], + 0, 0, + 0, 0, + 0, 0, + }; + + return copy_packages(code, pack, 3, space); +} + +static int relative_prime(int big, int little) +{ + int remainder; + + while (little != 0) { + remainder = big % little; + big = little; + little = remainder; + } + return big; +} + +static int avsync_to_package(struct go7007 *go, __le16 *code, int space) +{ + int arate = go->board_info->audio_rate * 1001 * go->fps_scale; + int ratio = arate / go->sensor_framerate; + int adjratio = ratio * 215 / 100; + int rprime = relative_prime(go->sensor_framerate, + arate % go->sensor_framerate); + int f1 = (arate % go->sensor_framerate) / rprime; + int f2 = (go->sensor_framerate - arate % go->sensor_framerate) / rprime; + u16 pack[] = { + 0x200e, 0, + 0xbf98, (u16)((-adjratio) & 0xffff), + 0xbf99, (u16)((-adjratio) >> 16), + 0xbf92, 0, + 0xbf93, 0, + 0xbff4, f1 > f2 ? f1 : f2, + 0xbff5, f1 < f2 ? f1 : f2, + 0xbff6, f1 < f2 ? ratio : ratio + 1, + 0xbff7, f1 > f2 ? ratio : ratio + 1, + 0xbff8, 0, + 0xbff9, 0, + 0xbffa, adjratio & 0xffff, + 0xbffb, adjratio >> 16, + 0xbf94, 0, + 0xbf95, 0, + 0, 0, + }; + + return copy_packages(code, pack, 1, space); +} + +static int final_package(struct go7007 *go, __le16 *code, int space) +{ + int rows = go->interlace_coding ? go->height / 32 : go->height / 16; + u16 pack[] = { + 0x8000, + 0, + 0, + 0, + 0, + 0, + 0, + 2, + ((go->board_info->sensor_flags & GO7007_SENSOR_TV) && + (!go->interlace_coding) ? + (1 << 14) | (1 << 9) : 0) | + ((go->encoder_subsample ? 1 : 0) << 8) | + (go->board_info->sensor_flags & + GO7007_SENSOR_CONFIG_MASK), + ((go->encoder_v_halve ? 1 : 0) << 14) | + (go->encoder_v_halve ? rows << 9 : rows << 8) | + (go->encoder_h_halve ? 1 << 6 : 0) | + (go->encoder_h_halve ? go->width >> 3 : go->width >> 4), + (1 << 15) | (go->encoder_v_offset << 6) | + (1 << 7) | (go->encoder_h_offset >> 2), + (1 << 6), + 0, + 0, + ((go->fps_scale - 1) << 8) | + (go->board_info->sensor_flags & GO7007_SENSOR_TV ? + (1 << 7) : 0) | + 0x41, + go->ipb ? 0xd4c : 0x36b, + (rows << 8) | (go->width >> 4), + go->format == GO7007_FORMAT_MPEG4 ? 0x0404 : 0, + (1 << 15) | ((go->interlace_coding ? 1 : 0) << 13) | + ((go->closed_gop ? 1 : 0) << 12) | + ((go->format == GO7007_FORMAT_MPEG4 ? 1 : 0) << 11) | + /* (1 << 9) | */ + ((go->ipb ? 3 : 0) << 7) | + ((go->modet_enable ? 1 : 0) << 2) | + ((go->dvd_mode ? 1 : 0) << 1) | 1, + (go->format == GO7007_FORMAT_MPEG1 ? 0x89a0 : + (go->format == GO7007_FORMAT_MPEG2 ? 0x89a0 : + (go->format == GO7007_FORMAT_MJPEG ? 0x89a0 : + (go->format == GO7007_FORMAT_MPEG4 ? 0x8920 : + (go->format == GO7007_FORMAT_H263 ? 0x8920 : 0))))), + go->ipb ? 0x1f15 : 0x1f0b, + go->ipb ? 0x0015 : 0x000b, + go->ipb ? 0xa800 : 0x5800, + 0xffff, + 0x0020 + 0x034b * 0, + 0x0020 + 0x034b * 1, + 0x0020 + 0x034b * 2, + 0x0020 + 0x034b * 3, + 0x0020 + 0x034b * 4, + 0x0020 + 0x034b * 5, + go->ipb ? (go->gop_size / 3) : go->gop_size, + (go->height >> 4) * (go->width >> 4) * 110 / 100, + }; + + return copy_packages(code, pack, 1, space); +} + +static int audio_to_package(struct go7007 *go, __le16 *code, int space) +{ + int clock_config = ((go->board_info->audio_flags & + GO7007_AUDIO_I2S_MASTER ? 1 : 0) << 11) | + ((go->board_info->audio_flags & + GO7007_AUDIO_OKI_MODE ? 1 : 0) << 8) | + (((go->board_info->audio_bclk_div / 4) - 1) << 4) | + (go->board_info->audio_main_div - 1); + u16 pack[] = { + 0x200d, 0, + 0x9002, 0, + 0x9002, 0, + 0x9031, 0, + 0x9032, 0, + 0x9033, 0, + 0x9034, 0, + 0x9035, 0, + 0x9036, 0, + 0x9037, 0, + 0x9040, 0, + 0x9000, clock_config, + 0x9001, (go->board_info->audio_flags & 0xffff) | + (1 << 9), + 0x9000, ((go->board_info->audio_flags & + GO7007_AUDIO_I2S_MASTER ? + 1 : 0) << 10) | + clock_config, + 0, 0, + 0, 0, + 0x2005, 0, + 0x9041, 0, + 0x9042, 256, + 0x9043, 0, + 0x9044, 16, + 0x9045, 16, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + 0, 0, + }; + + return copy_packages(code, pack, 2, space); +} + +static int modet_to_package(struct go7007 *go, __le16 *code, int space) +{ + int ret, mb, i, addr, cnt = 0; + u16 pack[32]; + u16 thresholds[] = { + 0x200e, 0, + 0xbf82, go->modet[0].pixel_threshold, + 0xbf83, go->modet[1].pixel_threshold, + 0xbf84, go->modet[2].pixel_threshold, + 0xbf85, go->modet[3].pixel_threshold, + 0xbf86, go->modet[0].motion_threshold, + 0xbf87, go->modet[1].motion_threshold, + 0xbf88, go->modet[2].motion_threshold, + 0xbf89, go->modet[3].motion_threshold, + 0xbf8a, go->modet[0].mb_threshold, + 0xbf8b, go->modet[1].mb_threshold, + 0xbf8c, go->modet[2].mb_threshold, + 0xbf8d, go->modet[3].mb_threshold, + 0xbf8e, 0, + 0xbf8f, 0, + 0, 0, + }; + + ret = copy_packages(code, thresholds, 1, space); + if (ret < 0) + return -1; + cnt += ret; + + addr = 0xbac0; + memset(pack, 0, 64); + i = 0; + for (mb = 0; mb < 1624; ++mb) { + pack[i * 2 + 3] <<= 2; + pack[i * 2 + 3] |= go->modet_map[mb]; + if (mb % 8 != 7) + continue; + pack[i * 2 + 2] = addr++; + ++i; + if (i == 10 || mb == 1623) { + pack[0] = 0x2000 | i; + ret = copy_packages(code + cnt, pack, 1, space - cnt); + if (ret < 0) + return -1; + cnt += ret; + i = 0; + memset(pack, 0, 64); + } + pack[i * 2 + 3] = 0; + } + + memset(pack, 0, 64); + i = 0; + for (addr = 0xbb90; addr < 0xbbfa; ++addr) { + pack[i * 2 + 2] = addr; + pack[i * 2 + 3] = 0; + ++i; + if (i == 10 || addr == 0xbbf9) { + pack[0] = 0x2000 | i; + ret = copy_packages(code + cnt, pack, 1, space - cnt); + if (ret < 0) + return -1; + cnt += ret; + i = 0; + memset(pack, 0, 64); + } + } + return cnt; +} + +static int do_special(struct go7007 *go, u16 type, __le16 *code, int space, + int *framelen) +{ + switch (type) { + case SPECIAL_FRM_HEAD: + switch (go->format) { + case GO7007_FORMAT_MJPEG: + return gen_mjpeghdr_to_package(go, code, space); + case GO7007_FORMAT_MPEG1: + case GO7007_FORMAT_MPEG2: + return gen_mpeg1hdr_to_package(go, code, space, + framelen); + case GO7007_FORMAT_MPEG4: + return gen_mpeg4hdr_to_package(go, code, space, + framelen); + } + case SPECIAL_BRC_CTRL: + return brctrl_to_package(go, code, space, framelen); + case SPECIAL_CONFIG: + return config_package(go, code, space); + case SPECIAL_SEQHEAD: + switch (go->format) { + case GO7007_FORMAT_MPEG1: + case GO7007_FORMAT_MPEG2: + return seqhead_to_package(go, code, space, + mpeg1_sequence_header); + case GO7007_FORMAT_MPEG4: + return seqhead_to_package(go, code, space, + mpeg4_sequence_header); + default: + return 0; + } + case SPECIAL_AV_SYNC: + return avsync_to_package(go, code, space); + case SPECIAL_FINAL: + return final_package(go, code, space); + case SPECIAL_AUDIO: + return audio_to_package(go, code, space); + case SPECIAL_MODET: + return modet_to_package(go, code, space); + } + printk(KERN_ERR + "go7007: firmware file contains unsupported feature %04x\n", + type); + return -1; +} + +int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen) +{ + const struct firmware *fw_entry; + __le16 *code, *src; + int framelen[8] = { }; /* holds the lengths of empty frame templates */ + int codespace = 64 * 1024, i = 0, srclen, chunk_len, chunk_flags; + int mode_flag; + int ret; + + switch (go->format) { + case GO7007_FORMAT_MJPEG: + mode_flag = FLAG_MODE_MJPEG; + break; + case GO7007_FORMAT_MPEG1: + mode_flag = FLAG_MODE_MPEG1; + break; + case GO7007_FORMAT_MPEG2: + mode_flag = FLAG_MODE_MPEG2; + break; + case GO7007_FORMAT_MPEG4: + mode_flag = FLAG_MODE_MPEG4; + break; + default: + return -1; + } + if (request_firmware(&fw_entry, go->board_info->firmware, go->dev)) { + printk(KERN_ERR + "go7007: unable to load firmware from file \"%s\"\n", + go->board_info->firmware); + return -1; + } + code = kzalloc(codespace * 2, GFP_KERNEL); + if (code == NULL) { + printk(KERN_ERR "go7007: unable to allocate %d bytes for " + "firmware construction\n", codespace * 2); + goto fw_failed; + } + src = (__le16 *)fw_entry->data; + srclen = fw_entry->size / 2; + while (srclen >= 2) { + chunk_flags = __le16_to_cpu(src[0]); + chunk_len = __le16_to_cpu(src[1]); + if (chunk_len + 2 > srclen) { + printk(KERN_ERR "go7007: firmware file \"%s\" " + "appears to be corrupted\n", + go->board_info->firmware); + goto fw_failed; + } + if (chunk_flags & mode_flag) { + if (chunk_flags & FLAG_SPECIAL) { + ret = do_special(go, __le16_to_cpu(src[2]), + &code[i], codespace - i, framelen); + if (ret < 0) { + printk(KERN_ERR "go7007: insufficient " + "memory for firmware " + "construction\n"); + goto fw_failed; + } + i += ret; + } else { + if (codespace - i < chunk_len) { + printk(KERN_ERR "go7007: insufficient " + "memory for firmware " + "construction\n"); + goto fw_failed; + } + memcpy(&code[i], &src[2], chunk_len * 2); + i += chunk_len; + } + } + srclen -= chunk_len + 2; + src += chunk_len + 2; + } + release_firmware(fw_entry); + *fw = (u8 *)code; + *fwlen = i * 2; + return 0; + +fw_failed: + kfree(code); + release_firmware(fw_entry); + return -1; +} diff --git a/drivers/staging/media/go7007/go7007-i2c.c b/drivers/staging/media/go7007/go7007-i2c.c new file mode 100644 index 000000000000..b8cfa1a6eaeb --- /dev/null +++ b/drivers/staging/media/go7007/go7007-i2c.c @@ -0,0 +1,225 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" +#include "wis-i2c.h" + +/********************* Driver for on-board I2C adapter *********************/ + +/* #define GO7007_I2C_DEBUG */ + +#define SPI_I2C_ADDR_BASE 0x1400 +#define STATUS_REG_ADDR (SPI_I2C_ADDR_BASE + 0x2) +#define I2C_CTRL_REG_ADDR (SPI_I2C_ADDR_BASE + 0x6) +#define I2C_DEV_UP_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x7) +#define I2C_LO_ADDR_REG_ADDR (SPI_I2C_ADDR_BASE + 0x8) +#define I2C_DATA_REG_ADDR (SPI_I2C_ADDR_BASE + 0x9) +#define I2C_CLKFREQ_REG_ADDR (SPI_I2C_ADDR_BASE + 0xa) + +#define I2C_STATE_MASK 0x0007 +#define I2C_READ_READY_MASK 0x0008 + +/* There is only one I2C port on the TW2804 that feeds all four GO7007 VIPs + * on the Adlink PCI-MPG24, so access is shared between all of them. */ +static DEFINE_MUTEX(adlink_mpg24_i2c_lock); + +static int go7007_i2c_xfer(struct go7007 *go, u16 addr, int read, + u16 command, int flags, u8 *data) +{ + int i, ret = -1; + u16 val; + + if (go->status == STATUS_SHUTDOWN) + return -1; + +#ifdef GO7007_I2C_DEBUG + if (read) + printk(KERN_DEBUG "go7007-i2c: reading 0x%02x on 0x%02x\n", + command, addr); + else + printk(KERN_DEBUG + "go7007-i2c: writing 0x%02x to 0x%02x on 0x%02x\n", + *data, command, addr); +#endif + + mutex_lock(&go->hw_lock); + + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + /* Bridge the I2C port on this GO7007 to the shared bus */ + mutex_lock(&adlink_mpg24_i2c_lock); + go7007_write_addr(go, 0x3c82, 0x0020); + } + + /* Wait for I2C adapter to be ready */ + for (i = 0; i < 10; ++i) { + if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) + goto i2c_done; + if (!(val & I2C_STATE_MASK)) + break; + msleep(100); + } + if (i == 10) { + printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + goto i2c_done; + } + + /* Set target register (command) */ + go7007_write_addr(go, I2C_CTRL_REG_ADDR, flags); + go7007_write_addr(go, I2C_LO_ADDR_REG_ADDR, command); + + /* If we're writing, send the data and target address and we're done */ + if (!read) { + go7007_write_addr(go, I2C_DATA_REG_ADDR, *data); + go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, + (addr << 9) | (command >> 8)); + ret = 0; + goto i2c_done; + } + + /* Otherwise, we're reading. First clear i2c_rx_data_rdy. */ + if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) + goto i2c_done; + + /* Send the target address plus read flag */ + go7007_write_addr(go, I2C_DEV_UP_ADDR_REG_ADDR, + (addr << 9) | 0x0100 | (command >> 8)); + + /* Wait for i2c_rx_data_rdy */ + for (i = 0; i < 10; ++i) { + if (go7007_read_addr(go, STATUS_REG_ADDR, &val) < 0) + goto i2c_done; + if (val & I2C_READ_READY_MASK) + break; + msleep(100); + } + if (i == 10) { + printk(KERN_ERR "go7007-i2c: I2C adapter is hung\n"); + goto i2c_done; + } + + /* Retrieve the read byte */ + if (go7007_read_addr(go, I2C_DATA_REG_ADDR, &val) < 0) + goto i2c_done; + *data = val; + ret = 0; + +i2c_done: + if (go->board_id == GO7007_BOARDID_ADLINK_MPG24) { + /* Isolate the I2C port on this GO7007 from the shared bus */ + go7007_write_addr(go, 0x3c82, 0x0000); + mutex_unlock(&adlink_mpg24_i2c_lock); + } + mutex_unlock(&go->hw_lock); + return ret; +} + +static int go7007_smbus_xfer(struct i2c_adapter *adapter, u16 addr, + unsigned short flags, char read_write, + u8 command, int size, union i2c_smbus_data *data) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + + if (size != I2C_SMBUS_BYTE_DATA) + return -1; + return go7007_i2c_xfer(go, addr, read_write == I2C_SMBUS_READ, command, + flags & I2C_CLIENT_SCCB ? 0x10 : 0x00, &data->byte); +} + +/* VERY LIMITED I2C master xfer function -- only needed because the + * SMBus functions only support 8-bit commands and the SAA7135 uses + * 16-bit commands. The I2C interface on the GO7007, as limited as + * it is, does support this mode. */ + +static int go7007_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + int i; + + for (i = 0; i < num; ++i) { + /* We can only do two things here -- write three bytes, or + * write two bytes and read one byte. */ + if (msgs[i].len == 2) { + if (i + 1 == num || msgs[i].addr != msgs[i + 1].addr || + (msgs[i].flags & I2C_M_RD) || + !(msgs[i + 1].flags & I2C_M_RD) || + msgs[i + 1].len != 1) + return -1; + if (go7007_i2c_xfer(go, msgs[i].addr, 1, + (msgs[i].buf[0] << 8) | msgs[i].buf[1], + 0x01, &msgs[i + 1].buf[0]) < 0) + return -1; + ++i; + } else if (msgs[i].len == 3) { + if (msgs[i].flags & I2C_M_RD) + return -1; + if (msgs[i].len != 3) + return -1; + if (go7007_i2c_xfer(go, msgs[i].addr, 0, + (msgs[i].buf[0] << 8) | msgs[i].buf[1], + 0x01, &msgs[i].buf[2]) < 0) + return -1; + } else + return -1; + } + + return 0; +} + +static u32 go7007_functionality(struct i2c_adapter *adapter) +{ + return I2C_FUNC_SMBUS_BYTE_DATA; +} + +static struct i2c_algorithm go7007_algo = { + .smbus_xfer = go7007_smbus_xfer, + .master_xfer = go7007_i2c_master_xfer, + .functionality = go7007_functionality, +}; + +static struct i2c_adapter go7007_adap_templ = { + .owner = THIS_MODULE, + .name = "WIS GO7007SB", + .algo = &go7007_algo, +}; + +int go7007_i2c_init(struct go7007 *go) +{ + memcpy(&go->i2c_adapter, &go7007_adap_templ, + sizeof(go7007_adap_templ)); + go->i2c_adapter.dev.parent = go->dev; + i2c_set_adapdata(&go->i2c_adapter, go); + if (i2c_add_adapter(&go->i2c_adapter) < 0) { + printk(KERN_ERR + "go7007-i2c: error: i2c_add_adapter failed\n"); + return -1; + } + return 0; +} diff --git a/drivers/staging/media/go7007/go7007-priv.h b/drivers/staging/media/go7007/go7007-priv.h new file mode 100644 index 000000000000..b58c394c6555 --- /dev/null +++ b/drivers/staging/media/go7007/go7007-priv.h @@ -0,0 +1,290 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +/* + * This is the private include file for the go7007 driver. It should not + * be included by anybody but the driver itself, and especially not by + * user-space applications. + */ + +#include + +struct go7007; + +/* IDs to activate board-specific support code */ +#define GO7007_BOARDID_MATRIX_II 0 +#define GO7007_BOARDID_MATRIX_RELOAD 1 +#define GO7007_BOARDID_STAR_TREK 2 +#define GO7007_BOARDID_PCI_VOYAGER 3 +#define GO7007_BOARDID_XMEN 4 +#define GO7007_BOARDID_XMEN_II 5 +#define GO7007_BOARDID_XMEN_III 6 +#define GO7007_BOARDID_MATRIX_REV 7 +#define GO7007_BOARDID_PX_M402U 16 +#define GO7007_BOARDID_PX_TV402U_ANY 17 /* need to check tuner model */ +#define GO7007_BOARDID_PX_TV402U_NA 18 /* detected NTSC tuner */ +#define GO7007_BOARDID_PX_TV402U_EU 19 /* detected PAL tuner */ +#define GO7007_BOARDID_PX_TV402U_JP 20 /* detected NTSC-J tuner */ +#define GO7007_BOARDID_LIFEVIEW_LR192 21 /* TV Walker Ultra */ +#define GO7007_BOARDID_ENDURA 22 +#define GO7007_BOARDID_ADLINK_MPG24 23 +#define GO7007_BOARDID_SENSORAY_2250 24 /* Sensoray 2250/2251 */ + +/* Various characteristics of each board */ +#define GO7007_BOARD_HAS_AUDIO (1<<0) +#define GO7007_BOARD_USE_ONBOARD_I2C (1<<1) +#define GO7007_BOARD_HAS_TUNER (1<<2) + +/* Characteristics of sensor devices */ +#define GO7007_SENSOR_VALID_POLAR (1<<0) +#define GO7007_SENSOR_HREF_POLAR (1<<1) +#define GO7007_SENSOR_VREF_POLAR (1<<2) +#define GO7007_SENSOR_FIELD_ID_POLAR (1<<3) +#define GO7007_SENSOR_BIT_WIDTH (1<<4) +#define GO7007_SENSOR_VALID_ENABLE (1<<5) +#define GO7007_SENSOR_656 (1<<6) +#define GO7007_SENSOR_CONFIG_MASK 0x7f +#define GO7007_SENSOR_TV (1<<7) +#define GO7007_SENSOR_VBI (1<<8) +#define GO7007_SENSOR_SCALING (1<<9) + +/* Characteristics of audio sensor devices */ +#define GO7007_AUDIO_I2S_MODE_1 (1) +#define GO7007_AUDIO_I2S_MODE_2 (2) +#define GO7007_AUDIO_I2S_MODE_3 (3) +#define GO7007_AUDIO_BCLK_POLAR (1<<2) +#define GO7007_AUDIO_WORD_14 (14<<4) +#define GO7007_AUDIO_WORD_16 (16<<4) +#define GO7007_AUDIO_ONE_CHANNEL (1<<11) +#define GO7007_AUDIO_I2S_MASTER (1<<16) +#define GO7007_AUDIO_OKI_MODE (1<<17) + +struct go7007_board_info { + char *firmware; + unsigned int flags; + int hpi_buffer_cap; + unsigned int sensor_flags; + int sensor_width; + int sensor_height; + int sensor_framerate; + int sensor_h_offset; + int sensor_v_offset; + unsigned int audio_flags; + int audio_rate; + int audio_bclk_div; + int audio_main_div; + int num_i2c_devs; + struct { + const char *type; + int id; + int addr; + } i2c_devs[4]; + int num_inputs; + struct { + int video_input; + int audio_input; + char *name; + } inputs[4]; +}; + +struct go7007_hpi_ops { + int (*interface_reset)(struct go7007 *go); + int (*write_interrupt)(struct go7007 *go, int addr, int data); + int (*read_interrupt)(struct go7007 *go); + int (*stream_start)(struct go7007 *go); + int (*stream_stop)(struct go7007 *go); + int (*send_firmware)(struct go7007 *go, u8 *data, int len); + int (*send_command)(struct go7007 *go, unsigned int cmd, void *arg); +}; + +/* The video buffer size must be a multiple of PAGE_SIZE */ +#define GO7007_BUF_PAGES (128 * 1024 / PAGE_SIZE) +#define GO7007_BUF_SIZE (GO7007_BUF_PAGES << PAGE_SHIFT) + +struct go7007_buffer { + struct go7007 *go; /* Reverse reference for VMA ops */ + int index; /* Reverse reference for DQBUF */ + enum { BUF_STATE_IDLE, BUF_STATE_QUEUED, BUF_STATE_DONE } state; + u32 seq; + struct timeval timestamp; + struct list_head stream; + struct page *pages[GO7007_BUF_PAGES + 1]; /* extra for straddling */ + unsigned long user_addr; + unsigned int page_count; + unsigned int offset; + unsigned int bytesused; + unsigned int frame_offset; + u32 modet_active; + int mapped; +}; + +struct go7007_file { + struct go7007 *go; + struct mutex lock; + int buf_count; + struct go7007_buffer *bufs; +}; + +#define GO7007_FORMAT_MJPEG 0 +#define GO7007_FORMAT_MPEG4 1 +#define GO7007_FORMAT_MPEG1 2 +#define GO7007_FORMAT_MPEG2 3 +#define GO7007_FORMAT_H263 4 + +#define GO7007_RATIO_1_1 0 +#define GO7007_RATIO_4_3 1 +#define GO7007_RATIO_16_9 2 + +enum go7007_parser_state { + STATE_DATA, + STATE_00, + STATE_00_00, + STATE_00_00_01, + STATE_FF, + STATE_VBI_LEN_A, + STATE_VBI_LEN_B, + STATE_MODET_MAP, + STATE_UNPARSED, +}; + +struct go7007 { + struct device *dev; + struct go7007_board_info *board_info; + unsigned int board_id; + int tuner_type; + int channel_number; /* for multi-channel boards like Adlink PCI-MPG24 */ + char name[64]; + struct video_device *video_dev; + struct v4l2_device v4l2_dev; + int ref_count; + enum { STATUS_INIT, STATUS_ONLINE, STATUS_SHUTDOWN } status; + spinlock_t spinlock; + struct mutex hw_lock; + int streaming; + int in_use; + int audio_enabled; + + /* Video input */ + int input; + enum { GO7007_STD_NTSC, GO7007_STD_PAL, GO7007_STD_OTHER } standard; + int sensor_framerate; + int width; + int height; + int encoder_h_offset; + int encoder_v_offset; + unsigned int encoder_h_halve:1; + unsigned int encoder_v_halve:1; + unsigned int encoder_subsample:1; + + /* Encoder config */ + int format; + int bitrate; + int fps_scale; + int pali; + int aspect_ratio; + int gop_size; + unsigned int ipb:1; + unsigned int closed_gop:1; + unsigned int repeat_seqhead:1; + unsigned int seq_header_enable:1; + unsigned int gop_header_enable:1; + unsigned int dvd_mode:1; + unsigned int interlace_coding:1; + + /* Motion detection */ + unsigned int modet_enable:1; + struct { + unsigned int enable:1; + int pixel_threshold; + int motion_threshold; + int mb_threshold; + } modet[4]; + unsigned char modet_map[1624]; + unsigned char active_map[216]; + + /* Video streaming */ + struct go7007_buffer *active_buf; + enum go7007_parser_state state; + int parse_length; + u16 modet_word; + int seen_frame; + u32 next_seq; + struct list_head stream; + wait_queue_head_t frame_waitq; + + /* Audio streaming */ + void (*audio_deliver)(struct go7007 *go, u8 *buf, int length); + void *snd_context; + + /* I2C */ + int i2c_adapter_online; + struct i2c_adapter i2c_adapter; + + /* HPI driver */ + struct go7007_hpi_ops *hpi_ops; + void *hpi_context; + int interrupt_available; + wait_queue_head_t interrupt_waitq; + unsigned short interrupt_value; + unsigned short interrupt_data; +}; + +static inline struct go7007 *to_go7007(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct go7007, v4l2_dev); +} + +/* All of these must be called with the hpi_lock mutex held! */ +#define go7007_interface_reset(go) \ + ((go)->hpi_ops->interface_reset(go)) +#define go7007_write_interrupt(go, x, y) \ + ((go)->hpi_ops->write_interrupt)((go), (x), (y)) +#define go7007_stream_start(go) \ + ((go)->hpi_ops->stream_start(go)) +#define go7007_stream_stop(go) \ + ((go)->hpi_ops->stream_stop(go)) +#define go7007_send_firmware(go, x, y) \ + ((go)->hpi_ops->send_firmware)((go), (x), (y)) +#define go7007_write_addr(go, x, y) \ + ((go)->hpi_ops->write_interrupt)((go), (x)|0x8000, (y)) + +/* go7007-driver.c */ +int go7007_read_addr(struct go7007 *go, u16 addr, u16 *data); +int go7007_read_interrupt(struct go7007 *go, u16 *value, u16 *data); +int go7007_boot_encoder(struct go7007 *go, int init_i2c); +int go7007_reset_encoder(struct go7007 *go); +int go7007_register_encoder(struct go7007 *go); +int go7007_start_encoder(struct go7007 *go); +void go7007_parse_video_stream(struct go7007 *go, u8 *buf, int length); +struct go7007 *go7007_alloc(struct go7007_board_info *board, + struct device *dev); +void go7007_remove(struct go7007 *go); + +/* go7007-fw.c */ +int go7007_construct_fw_image(struct go7007 *go, u8 **fw, int *fwlen); + +/* go7007-i2c.c */ +int go7007_i2c_init(struct go7007 *go); +int go7007_i2c_remove(struct go7007 *go); + +/* go7007-v4l2.c */ +int go7007_v4l2_init(struct go7007 *go); +void go7007_v4l2_remove(struct go7007 *go); + +/* snd-go7007.c */ +int go7007_snd_init(struct go7007 *go); +int go7007_snd_remove(struct go7007 *go); diff --git a/drivers/staging/media/go7007/go7007-usb.c b/drivers/staging/media/go7007/go7007-usb.c new file mode 100644 index 000000000000..3db3b0a91cc1 --- /dev/null +++ b/drivers/staging/media/go7007/go7007-usb.c @@ -0,0 +1,1288 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" +#include "wis-i2c.h" + +static unsigned int assume_endura; +module_param(assume_endura, int, 0644); +MODULE_PARM_DESC(assume_endura, "when probing fails, " + "hardware is a Pelco Endura"); + +/* #define GO7007_USB_DEBUG */ +/* #define GO7007_I2C_DEBUG */ /* for debugging the EZ-USB I2C adapter */ + +#define HPI_STATUS_ADDR 0xFFF4 +#define INT_PARAM_ADDR 0xFFF6 +#define INT_INDEX_ADDR 0xFFF8 + +/* + * Pipes on EZ-USB interface: + * 0 snd - Control + * 0 rcv - Control + * 2 snd - Download firmware (control) + * 4 rcv - Read Interrupt (interrupt) + * 6 rcv - Read Video (bulk) + * 8 rcv - Read Audio (bulk) + */ + +#define GO7007_USB_EZUSB (1<<0) +#define GO7007_USB_EZUSB_I2C (1<<1) + +struct go7007_usb_board { + unsigned int flags; + struct go7007_board_info main_info; +}; + +struct go7007_usb { + struct go7007_usb_board *board; + struct mutex i2c_lock; + struct usb_device *usbdev; + struct urb *video_urbs[8]; + struct urb *audio_urbs[8]; + struct urb *intr_urb; +}; + +/*********************** Product specification data ***********************/ + +static struct go7007_usb_board board_matrix_ii = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "wis_saa7115", + .id = I2C_DRIVERID_WIS_SAA7115, + .addr = 0x20, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 9, + .name = "S-Video", + }, + }, + }, +}; + +static struct go7007_usb_board board_matrix_reload = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "wis_saa7113", + .id = I2C_DRIVERID_WIS_SAA7113, + .addr = 0x25, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 9, + .name = "S-Video", + }, + }, + }, +}; + +static struct go7007_usb_board board_star_trek = { + .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_HAS_AUDIO, /* | + GO7007_BOARD_HAS_TUNER, */ + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "wis_saa7115", + .id = I2C_DRIVERID_WIS_SAA7115, + .addr = 0x20, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 1, + /* .audio_input = AUDIO_EXTERN, */ + .name = "Composite", + }, + { + .video_input = 8, + /* .audio_input = AUDIO_EXTERN, */ + .name = "S-Video", + }, + /* { + * .video_input = 3, + * .audio_input = AUDIO_TUNER, + * .name = "Tuner", + * }, + */ + }, + }, +}; + +static struct go7007_usb_board board_px_tv402u = { + .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_HAS_TUNER, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .num_i2c_devs = 3, + .i2c_devs = { + { + .type = "wis_saa7115", + .id = I2C_DRIVERID_WIS_SAA7115, + .addr = 0x20, + }, + { + .type = "wis_uda1342", + .id = I2C_DRIVERID_WIS_UDA1342, + .addr = 0x1a, + }, + { + .type = "wis_sony_tuner", + .id = I2C_DRIVERID_WIS_SONY_TUNER, + .addr = 0x60, + }, + }, + .num_inputs = 3, + .inputs = { + { + .video_input = 1, + .audio_input = TVAUDIO_INPUT_EXTERN, + .name = "Composite", + }, + { + .video_input = 8, + .audio_input = TVAUDIO_INPUT_EXTERN, + .name = "S-Video", + }, + { + .video_input = 3, + .audio_input = TVAUDIO_INPUT_TUNER, + .name = "Tuner", + }, + }, + }, +}; + +static struct go7007_usb_board board_xmen = { + .flags = 0, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_USE_ONBOARD_I2C, + .hpi_buffer_cap = 0, + .sensor_flags = GO7007_SENSOR_VREF_POLAR, + .sensor_width = 320, + .sensor_height = 240, + .sensor_framerate = 30030, + .audio_flags = GO7007_AUDIO_ONE_CHANNEL | + GO7007_AUDIO_I2S_MODE_3 | + GO7007_AUDIO_WORD_14 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_BCLK_POLAR | + GO7007_AUDIO_OKI_MODE, + .audio_rate = 8000, + .audio_bclk_div = 48, + .audio_main_div = 1, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "wis_ov7640", + .id = I2C_DRIVERID_WIS_OV7640, + .addr = 0x21, + }, + }, + .num_inputs = 1, + .inputs = { + { + .name = "Camera", + }, + }, + }, +}; + +static struct go7007_usb_board board_matrix_revolution = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "wis_tw9903", + .id = I2C_DRIVERID_WIS_TW9903, + .addr = 0x44, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 2, + .name = "Composite", + }, + { + .video_input = 8, + .name = "S-Video", + }, + }, + }, +}; + +static struct go7007_usb_board board_lifeview_lr192 = { + .flags = GO7007_USB_EZUSB, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_HAS_AUDIO | + GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI | + GO7007_SENSOR_SCALING, + .num_i2c_devs = 0, + .num_inputs = 1, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + }, + }, +}; + +static struct go7007_usb_board board_endura = { + .flags = 0, + .main_info = { + .firmware = "go7007tv.bin", + .flags = 0, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 8000, + .audio_bclk_div = 48, + .audio_main_div = 8, + .hpi_buffer_cap = 0, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV, + .sensor_h_offset = 8, + .num_i2c_devs = 0, + .num_inputs = 1, + .inputs = { + { + .name = "Camera", + }, + }, + }, +}; + +static struct go7007_usb_board board_adlink_mpg24 = { + .flags = 0, + .main_info = { + .firmware = "go7007tv.bin", + .flags = GO7007_BOARD_USE_ONBOARD_I2C, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 0, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "wis_tw2804", + .id = I2C_DRIVERID_WIS_TW2804, + .addr = 0x00, /* yes, really */ + }, + }, + .num_inputs = 1, + .inputs = { + { + .name = "Composite", + }, + }, + }, +}; + +static struct go7007_usb_board board_sensoray_2250 = { + .flags = GO7007_USB_EZUSB | GO7007_USB_EZUSB_I2C, + .main_info = { + .firmware = "go7007tv.bin", + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_I2S_MASTER | + GO7007_AUDIO_WORD_16, + .flags = GO7007_BOARD_HAS_AUDIO, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_TV, + .num_i2c_devs = 1, + .i2c_devs = { + { + .type = "s2250", + .id = I2C_DRIVERID_S2250, + .addr = 0x43, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 1, + .name = "S-Video", + }, + }, + }, +}; + +MODULE_FIRMWARE("go7007tv.bin"); + +static const struct usb_device_id go7007_usb_id_table[] = { + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x200, /* Revision number of XMen */ + .bcdDevice_hi = 0x200, + .bInterfaceClass = 255, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 255, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x202, /* Revision number of Matrix II */ + .bcdDevice_hi = 0x202, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_II, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x204, /* Revision number of Matrix */ + .bcdDevice_hi = 0x204, /* Reloaded */ + .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_RELOAD, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x205, /* Revision number of XMen-II */ + .bcdDevice_hi = 0x205, + .bInterfaceClass = 255, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 255, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_II, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x208, /* Revision number of Star Trek */ + .bcdDevice_hi = 0x208, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_STAR_TREK, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION | + USB_DEVICE_ID_MATCH_INT_INFO, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x209, /* Revision number of XMen-III */ + .bcdDevice_hi = 0x209, + .bInterfaceClass = 255, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 255, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_XMEN_III, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x0eb1, /* Vendor ID of WIS Technologies */ + .idProduct = 0x7007, /* Product ID of GO7007SB chip */ + .bcdDevice_lo = 0x210, /* Revision number of Matrix */ + .bcdDevice_hi = 0x210, /* Revolution */ + .driver_info = (kernel_ulong_t)GO7007_BOARDID_MATRIX_REV, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x093b, /* Vendor ID of Plextor */ + .idProduct = 0xa102, /* Product ID of M402U */ + .bcdDevice_lo = 0x1, /* revision number of Blueberry */ + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_M402U, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x093b, /* Vendor ID of Plextor */ + .idProduct = 0xa104, /* Product ID of TV402U */ + .bcdDevice_lo = 0x1, + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_PX_TV402U_ANY, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x10fd, /* Vendor ID of Anubis Electronics */ + .idProduct = 0xde00, /* Product ID of Lifeview LR192 */ + .bcdDevice_lo = 0x1, + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_LIFEVIEW_LR192, + }, + { + .match_flags = USB_DEVICE_ID_MATCH_DEVICE_AND_VERSION, + .idVendor = 0x1943, /* Vendor ID Sensoray */ + .idProduct = 0x2250, /* Product ID of 2250/2251 */ + .bcdDevice_lo = 0x1, + .bcdDevice_hi = 0x1, + .driver_info = (kernel_ulong_t)GO7007_BOARDID_SENSORAY_2250, + }, + { } /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, go7007_usb_id_table); + +/********************* Driver for EZ-USB HPI interface *********************/ + +static int go7007_usb_vendor_request(struct go7007 *go, int request, + int value, int index, void *transfer_buffer, int length, int in) +{ + struct go7007_usb *usb = go->hpi_context; + int timeout = 5000; + + if (in) { + return usb_control_msg(usb->usbdev, + usb_rcvctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + value, index, transfer_buffer, length, timeout); + } else { + return usb_control_msg(usb->usbdev, + usb_sndctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, transfer_buffer, length, timeout); + } +} + +static int go7007_usb_interface_reset(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + u16 intr_val, intr_data; + + /* Reset encoder */ + if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) + return -1; + msleep(100); + + if (usb->board->flags & GO7007_USB_EZUSB) { + /* Reset buffer in EZ-USB */ +#ifdef GO7007_USB_DEBUG + printk(KERN_DEBUG "go7007-usb: resetting EZ-USB buffers\n"); +#endif + if (go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0 || + go7007_usb_vendor_request(go, 0x10, 0, 0, NULL, 0, 0) < 0) + return -1; + + /* Reset encoder again */ + if (go7007_write_interrupt(go, 0x0001, 0x0001) < 0) + return -1; + msleep(100); + } + + /* Wait for an interrupt to indicate successful hardware reset */ + if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + (intr_val & ~0x1) != 0x55aa) { + printk(KERN_ERR + "go7007-usb: unable to reset the USB interface\n"); + return -1; + } + return 0; +} + +static int go7007_usb_ezusb_write_interrupt(struct go7007 *go, + int addr, int data) +{ + struct go7007_usb *usb = go->hpi_context; + int i, r; + u16 status_reg; + int timeout = 500; + +#ifdef GO7007_USB_DEBUG + printk(KERN_DEBUG + "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data); +#endif + + for (i = 0; i < 100; ++i) { + r = usb_control_msg(usb->usbdev, + usb_rcvctrlpipe(usb->usbdev, 0), 0x14, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + 0, HPI_STATUS_ADDR, &status_reg, + sizeof(status_reg), timeout); + if (r < 0) + goto write_int_error; + __le16_to_cpus(&status_reg); + if (!(status_reg & 0x0010)) + break; + msleep(10); + } + if (i == 100) { + printk(KERN_ERR + "go7007-usb: device is hung, status reg = 0x%04x\n", + status_reg); + return -1; + } + r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), 0x12, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, data, + INT_PARAM_ADDR, NULL, 0, timeout); + if (r < 0) + goto write_int_error; + r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 0), + 0x12, USB_TYPE_VENDOR | USB_RECIP_DEVICE, addr, + INT_INDEX_ADDR, NULL, 0, timeout); + if (r < 0) + goto write_int_error; + return 0; + +write_int_error: + printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r); + return r; +} + +static int go7007_usb_onboard_write_interrupt(struct go7007 *go, + int addr, int data) +{ + struct go7007_usb *usb = go->hpi_context; + u8 *tbuf; + int r; + int timeout = 500; + +#ifdef GO7007_USB_DEBUG + printk(KERN_DEBUG + "go7007-usb: WriteInterrupt: %04x %04x\n", addr, data); +#endif + + tbuf = kzalloc(8, GFP_KERNEL); + if (tbuf == NULL) + return -ENOMEM; + tbuf[0] = data & 0xff; + tbuf[1] = data >> 8; + tbuf[2] = addr & 0xff; + tbuf[3] = addr >> 8; + r = usb_control_msg(usb->usbdev, usb_sndctrlpipe(usb->usbdev, 2), 0x00, + USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, 0x55aa, + 0xf0f0, tbuf, 8, timeout); + kfree(tbuf); + if (r < 0) { + printk(KERN_ERR "go7007-usb: error in WriteInterrupt: %d\n", r); + return r; + } + return 0; +} + +static void go7007_usb_readinterrupt_complete(struct urb *urb) +{ + struct go7007 *go = (struct go7007 *)urb->context; + u16 *regs = (u16 *)urb->transfer_buffer; + int status = urb->status; + + if (status) { + if (status != -ESHUTDOWN && + go->status != STATUS_SHUTDOWN) { + printk(KERN_ERR + "go7007-usb: error in read interrupt: %d\n", + urb->status); + } else { + wake_up(&go->interrupt_waitq); + return; + } + } else if (urb->actual_length != urb->transfer_buffer_length) { + printk(KERN_ERR "go7007-usb: short read in interrupt pipe!\n"); + } else { + go->interrupt_available = 1; + go->interrupt_data = __le16_to_cpu(regs[0]); + go->interrupt_value = __le16_to_cpu(regs[1]); +#ifdef GO7007_USB_DEBUG + printk(KERN_DEBUG "go7007-usb: ReadInterrupt: %04x %04x\n", + go->interrupt_value, go->interrupt_data); +#endif + } + + wake_up(&go->interrupt_waitq); +} + +static int go7007_usb_read_interrupt(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + int r; + + r = usb_submit_urb(usb->intr_urb, GFP_KERNEL); + if (r < 0) { + printk(KERN_ERR + "go7007-usb: unable to submit interrupt urb: %d\n", r); + return r; + } + return 0; +} + +static void go7007_usb_read_video_pipe_complete(struct urb *urb) +{ + struct go7007 *go = (struct go7007 *)urb->context; + int r, status = urb->status; + + if (!go->streaming) { + wake_up_interruptible(&go->frame_waitq); + return; + } + if (status) { + printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", + status); + return; + } + if (urb->actual_length != urb->transfer_buffer_length) { + printk(KERN_ERR "go7007-usb: short read in video pipe!\n"); + return; + } + go7007_parse_video_stream(go, urb->transfer_buffer, urb->actual_length); + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r < 0) + printk(KERN_ERR "go7007-usb: error in video pipe: %d\n", r); +} + +static void go7007_usb_read_audio_pipe_complete(struct urb *urb) +{ + struct go7007 *go = (struct go7007 *)urb->context; + int r, status = urb->status; + + if (!go->streaming) + return; + if (status) { + printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", + status); + return; + } + if (urb->actual_length != urb->transfer_buffer_length) { + printk(KERN_ERR "go7007-usb: short read in audio pipe!\n"); + return; + } + if (go->audio_deliver != NULL) + go->audio_deliver(go, urb->transfer_buffer, urb->actual_length); + r = usb_submit_urb(urb, GFP_ATOMIC); + if (r < 0) + printk(KERN_ERR "go7007-usb: error in audio pipe: %d\n", r); +} + +static int go7007_usb_stream_start(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + int i, r; + + for (i = 0; i < 8; ++i) { + r = usb_submit_urb(usb->video_urbs[i], GFP_KERNEL); + if (r < 0) { + printk(KERN_ERR "go7007-usb: error submitting video " + "urb %d: %d\n", i, r); + goto video_submit_failed; + } + } + if (!go->audio_enabled) + return 0; + + for (i = 0; i < 8; ++i) { + r = usb_submit_urb(usb->audio_urbs[i], GFP_KERNEL); + if (r < 0) { + printk(KERN_ERR "go7007-usb: error submitting audio " + "urb %d: %d\n", i, r); + goto audio_submit_failed; + } + } + return 0; + +audio_submit_failed: + for (i = 0; i < 7; ++i) + usb_kill_urb(usb->audio_urbs[i]); +video_submit_failed: + for (i = 0; i < 8; ++i) + usb_kill_urb(usb->video_urbs[i]); + return -1; +} + +static int go7007_usb_stream_stop(struct go7007 *go) +{ + struct go7007_usb *usb = go->hpi_context; + int i; + + if (go->status == STATUS_SHUTDOWN) + return 0; + for (i = 0; i < 8; ++i) + usb_kill_urb(usb->video_urbs[i]); + if (go->audio_enabled) + for (i = 0; i < 8; ++i) + usb_kill_urb(usb->audio_urbs[i]); + return 0; +} + +static int go7007_usb_send_firmware(struct go7007 *go, u8 *data, int len) +{ + struct go7007_usb *usb = go->hpi_context; + int transferred, pipe; + int timeout = 500; + +#ifdef GO7007_USB_DEBUG + printk(KERN_DEBUG "go7007-usb: DownloadBuffer sending %d bytes\n", len); +#endif + + if (usb->board->flags & GO7007_USB_EZUSB) + pipe = usb_sndbulkpipe(usb->usbdev, 2); + else + pipe = usb_sndbulkpipe(usb->usbdev, 3); + + return usb_bulk_msg(usb->usbdev, pipe, data, len, + &transferred, timeout); +} + +static struct go7007_hpi_ops go7007_usb_ezusb_hpi_ops = { + .interface_reset = go7007_usb_interface_reset, + .write_interrupt = go7007_usb_ezusb_write_interrupt, + .read_interrupt = go7007_usb_read_interrupt, + .stream_start = go7007_usb_stream_start, + .stream_stop = go7007_usb_stream_stop, + .send_firmware = go7007_usb_send_firmware, +}; + +static struct go7007_hpi_ops go7007_usb_onboard_hpi_ops = { + .interface_reset = go7007_usb_interface_reset, + .write_interrupt = go7007_usb_onboard_write_interrupt, + .read_interrupt = go7007_usb_read_interrupt, + .stream_start = go7007_usb_stream_start, + .stream_stop = go7007_usb_stream_stop, + .send_firmware = go7007_usb_send_firmware, +}; + +/********************* Driver for EZ-USB I2C adapter *********************/ + +static int go7007_usb_i2c_master_xfer(struct i2c_adapter *adapter, + struct i2c_msg msgs[], int num) +{ + struct go7007 *go = i2c_get_adapdata(adapter); + struct go7007_usb *usb = go->hpi_context; + u8 buf[16]; + int buf_len, i; + int ret = -1; + + if (go->status == STATUS_SHUTDOWN) + return -1; + + mutex_lock(&usb->i2c_lock); + + for (i = 0; i < num; ++i) { + /* The hardware command is "write some bytes then read some + * bytes", so we try to coalesce a write followed by a read + * into a single USB transaction */ + if (i + 1 < num && msgs[i].addr == msgs[i + 1].addr && + !(msgs[i].flags & I2C_M_RD) && + (msgs[i + 1].flags & I2C_M_RD)) { +#ifdef GO7007_I2C_DEBUG + printk(KERN_DEBUG "go7007-usb: i2c write/read %d/%d " + "bytes on %02x\n", msgs[i].len, + msgs[i + 1].len, msgs[i].addr); +#endif + buf[0] = 0x01; + buf[1] = msgs[i].len + 1; + buf[2] = msgs[i].addr << 1; + memcpy(&buf[3], msgs[i].buf, msgs[i].len); + buf_len = msgs[i].len + 3; + buf[buf_len++] = msgs[++i].len; + } else if (msgs[i].flags & I2C_M_RD) { +#ifdef GO7007_I2C_DEBUG + printk(KERN_DEBUG "go7007-usb: i2c read %d " + "bytes on %02x\n", msgs[i].len, + msgs[i].addr); +#endif + buf[0] = 0x01; + buf[1] = 1; + buf[2] = msgs[i].addr << 1; + buf[3] = msgs[i].len; + buf_len = 4; + } else { +#ifdef GO7007_I2C_DEBUG + printk(KERN_DEBUG "go7007-usb: i2c write %d " + "bytes on %02x\n", msgs[i].len, + msgs[i].addr); +#endif + buf[0] = 0x00; + buf[1] = msgs[i].len + 1; + buf[2] = msgs[i].addr << 1; + memcpy(&buf[3], msgs[i].buf, msgs[i].len); + buf_len = msgs[i].len + 3; + buf[buf_len++] = 0; + } + if (go7007_usb_vendor_request(go, 0x24, 0, 0, + buf, buf_len, 0) < 0) + goto i2c_done; + if (msgs[i].flags & I2C_M_RD) { + memset(buf, 0, sizeof(buf)); + if (go7007_usb_vendor_request(go, 0x25, 0, 0, buf, + msgs[i].len + 1, 1) < 0) + goto i2c_done; + memcpy(msgs[i].buf, buf + 1, msgs[i].len); + } + } + ret = 0; + +i2c_done: + mutex_unlock(&usb->i2c_lock); + return ret; +} + +static u32 go7007_usb_functionality(struct i2c_adapter *adapter) +{ + /* No errors are reported by the hardware, so we don't bother + * supporting quick writes to avoid confusing probing */ + return (I2C_FUNC_SMBUS_EMUL) & ~I2C_FUNC_SMBUS_QUICK; +} + +static struct i2c_algorithm go7007_usb_algo = { + .master_xfer = go7007_usb_i2c_master_xfer, + .functionality = go7007_usb_functionality, +}; + +static struct i2c_adapter go7007_usb_adap_templ = { + .owner = THIS_MODULE, + .name = "WIS GO7007SB EZ-USB", + .algo = &go7007_usb_algo, +}; + +/********************* USB add/remove functions *********************/ + +static int go7007_usb_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct go7007 *go; + struct go7007_usb *usb; + struct go7007_usb_board *board; + struct usb_device *usbdev = interface_to_usbdev(intf); + char *name; + int video_pipe, i, v_urb_len; + + printk(KERN_DEBUG "go7007-usb: probing new GO7007 USB board\n"); + + switch (id->driver_info) { + case GO7007_BOARDID_MATRIX_II: + name = "WIS Matrix II or compatible"; + board = &board_matrix_ii; + break; + case GO7007_BOARDID_MATRIX_RELOAD: + name = "WIS Matrix Reloaded or compatible"; + board = &board_matrix_reload; + break; + case GO7007_BOARDID_MATRIX_REV: + name = "WIS Matrix Revolution or compatible"; + board = &board_matrix_revolution; + break; + case GO7007_BOARDID_STAR_TREK: + name = "WIS Star Trek or compatible"; + board = &board_star_trek; + break; + case GO7007_BOARDID_XMEN: + name = "WIS XMen or compatible"; + board = &board_xmen; + break; + case GO7007_BOARDID_XMEN_II: + name = "WIS XMen II or compatible"; + board = &board_xmen; + break; + case GO7007_BOARDID_XMEN_III: + name = "WIS XMen III or compatible"; + board = &board_xmen; + break; + case GO7007_BOARDID_PX_M402U: + name = "Plextor PX-M402U"; + board = &board_matrix_ii; + break; + case GO7007_BOARDID_PX_TV402U_ANY: + name = "Plextor PX-TV402U (unknown tuner)"; + board = &board_px_tv402u; + break; + case GO7007_BOARDID_LIFEVIEW_LR192: + printk(KERN_ERR "go7007-usb: The Lifeview TV Walker Ultra " + "is not supported. Sorry!\n"); + return 0; + name = "Lifeview TV Walker Ultra"; + board = &board_lifeview_lr192; + break; + case GO7007_BOARDID_SENSORAY_2250: + printk(KERN_INFO "Sensoray 2250 found\n"); + name = "Sensoray 2250/2251"; + board = &board_sensoray_2250; + break; + default: + printk(KERN_ERR "go7007-usb: unknown board ID %d!\n", + (unsigned int)id->driver_info); + return 0; + } + + usb = kzalloc(sizeof(struct go7007_usb), GFP_KERNEL); + if (usb == NULL) + return -ENOMEM; + + /* Allocate the URB and buffer for receiving incoming interrupts */ + usb->intr_urb = usb_alloc_urb(0, GFP_KERNEL); + if (usb->intr_urb == NULL) + goto allocfail; + usb->intr_urb->transfer_buffer = kmalloc(2*sizeof(u16), GFP_KERNEL); + if (usb->intr_urb->transfer_buffer == NULL) + goto allocfail; + + go = go7007_alloc(&board->main_info, &intf->dev); + if (go == NULL) + goto allocfail; + usb->board = board; + usb->usbdev = usbdev; + go->board_id = id->driver_info; + strncpy(go->name, name, sizeof(go->name)); + if (board->flags & GO7007_USB_EZUSB) + go->hpi_ops = &go7007_usb_ezusb_hpi_ops; + else + go->hpi_ops = &go7007_usb_onboard_hpi_ops; + go->hpi_context = usb; + usb_fill_int_urb(usb->intr_urb, usb->usbdev, + usb_rcvintpipe(usb->usbdev, 4), + usb->intr_urb->transfer_buffer, 2*sizeof(u16), + go7007_usb_readinterrupt_complete, go, 8); + usb_set_intfdata(intf, &go->v4l2_dev); + + /* Boot the GO7007 */ + if (go7007_boot_encoder(go, go->board_info->flags & + GO7007_BOARD_USE_ONBOARD_I2C) < 0) + goto initfail; + + /* Register the EZ-USB I2C adapter, if we're using it */ + if (board->flags & GO7007_USB_EZUSB_I2C) { + memcpy(&go->i2c_adapter, &go7007_usb_adap_templ, + sizeof(go7007_usb_adap_templ)); + mutex_init(&usb->i2c_lock); + go->i2c_adapter.dev.parent = go->dev; + i2c_set_adapdata(&go->i2c_adapter, go); + if (i2c_add_adapter(&go->i2c_adapter) < 0) { + printk(KERN_ERR + "go7007-usb: error: i2c_add_adapter failed\n"); + goto initfail; + } + go->i2c_adapter_online = 1; + } + + /* Pelco and Adlink reused the XMen and XMen-III vendor and product + * IDs for their own incompatible designs. We can detect XMen boards + * by probing the sensor, but there is no way to probe the sensors on + * the Pelco and Adlink designs so we default to the Adlink. If it + * is actually a Pelco, the user must set the assume_endura module + * parameter. */ + if ((go->board_id == GO7007_BOARDID_XMEN || + go->board_id == GO7007_BOARDID_XMEN_III) && + go->i2c_adapter_online) { + union i2c_smbus_data data; + + /* Check to see if register 0x0A is 0x76 */ + i2c_smbus_xfer(&go->i2c_adapter, 0x21, I2C_CLIENT_SCCB, + I2C_SMBUS_READ, 0x0A, I2C_SMBUS_BYTE_DATA, &data); + if (data.byte != 0x76) { + if (assume_endura) { + go->board_id = GO7007_BOARDID_ENDURA; + usb->board = board = &board_endura; + go->board_info = &board->main_info; + strncpy(go->name, "Pelco Endura", + sizeof(go->name)); + } else { + u16 channel; + + /* set GPIO5 to be an output, currently low */ + go7007_write_addr(go, 0x3c82, 0x0000); + go7007_write_addr(go, 0x3c80, 0x00df); + /* read channel number from GPIO[1:0] */ + go7007_read_addr(go, 0x3c81, &channel); + channel &= 0x3; + go->board_id = GO7007_BOARDID_ADLINK_MPG24; + usb->board = board = &board_adlink_mpg24; + go->board_info = &board->main_info; + go->channel_number = channel; + snprintf(go->name, sizeof(go->name), + "Adlink PCI-MPG24, channel #%d", + channel); + } + } + } + + /* Probe the tuner model on the TV402U */ + if (go->board_id == GO7007_BOARDID_PX_TV402U_ANY) { + u8 data[3]; + + /* Board strapping indicates tuner model */ + if (go7007_usb_vendor_request(go, 0x41, 0, 0, data, 3, 1) < 0) { + printk(KERN_ERR "go7007-usb: GPIO read failed!\n"); + goto initfail; + } + switch (data[0] >> 6) { + case 1: + go->board_id = GO7007_BOARDID_PX_TV402U_EU; + go->tuner_type = TUNER_SONY_BTF_PG472Z; + strncpy(go->name, "Plextor PX-TV402U-EU", + sizeof(go->name)); + break; + case 2: + go->board_id = GO7007_BOARDID_PX_TV402U_JP; + go->tuner_type = TUNER_SONY_BTF_PK467Z; + strncpy(go->name, "Plextor PX-TV402U-JP", + sizeof(go->name)); + break; + case 3: + go->board_id = GO7007_BOARDID_PX_TV402U_NA; + go->tuner_type = TUNER_SONY_BTF_PB463Z; + strncpy(go->name, "Plextor PX-TV402U-NA", + sizeof(go->name)); + break; + default: + printk(KERN_DEBUG "go7007-usb: unable to detect " + "tuner type!\n"); + break; + } + /* Configure tuner mode selection inputs connected + * to the EZ-USB GPIO output pins */ + if (go7007_usb_vendor_request(go, 0x40, 0x7f02, 0, + NULL, 0, 0) < 0) { + printk(KERN_ERR "go7007-usb: GPIO write failed!\n"); + goto initfail; + } + } + + /* Print a nasty message if the user attempts to use a USB2.0 device in + * a USB1.1 port. There will be silent corruption of the stream. */ + if ((board->flags & GO7007_USB_EZUSB) && + usbdev->speed != USB_SPEED_HIGH) + printk(KERN_ERR "go7007-usb: *** WARNING *** This device " + "must be connected to a USB 2.0 port! " + "Attempting to capture video through a USB 1.1 " + "port will result in stream corruption, even " + "at low bitrates!\n"); + + /* Do any final GO7007 initialization, then register the + * V4L2 and ALSA interfaces */ + if (go7007_register_encoder(go) < 0) + goto initfail; + + /* Allocate the URBs and buffers for receiving the video stream */ + if (board->flags & GO7007_USB_EZUSB) { + v_urb_len = 1024; + video_pipe = usb_rcvbulkpipe(usb->usbdev, 6); + } else { + v_urb_len = 512; + video_pipe = usb_rcvbulkpipe(usb->usbdev, 1); + } + for (i = 0; i < 8; ++i) { + usb->video_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (usb->video_urbs[i] == NULL) + goto initfail; + usb->video_urbs[i]->transfer_buffer = + kmalloc(v_urb_len, GFP_KERNEL); + if (usb->video_urbs[i]->transfer_buffer == NULL) + goto initfail; + usb_fill_bulk_urb(usb->video_urbs[i], usb->usbdev, video_pipe, + usb->video_urbs[i]->transfer_buffer, v_urb_len, + go7007_usb_read_video_pipe_complete, go); + } + + /* Allocate the URBs and buffers for receiving the audio stream */ + if ((board->flags & GO7007_USB_EZUSB) && go->audio_enabled) + for (i = 0; i < 8; ++i) { + usb->audio_urbs[i] = usb_alloc_urb(0, GFP_KERNEL); + if (usb->audio_urbs[i] == NULL) + goto initfail; + usb->audio_urbs[i]->transfer_buffer = kmalloc(4096, + GFP_KERNEL); + if (usb->audio_urbs[i]->transfer_buffer == NULL) + goto initfail; + usb_fill_bulk_urb(usb->audio_urbs[i], usb->usbdev, + usb_rcvbulkpipe(usb->usbdev, 8), + usb->audio_urbs[i]->transfer_buffer, 4096, + go7007_usb_read_audio_pipe_complete, go); + } + + + go->status = STATUS_ONLINE; + return 0; + +initfail: + go->status = STATUS_SHUTDOWN; + return 0; + +allocfail: + if (usb->intr_urb) { + kfree(usb->intr_urb->transfer_buffer); + usb_free_urb(usb->intr_urb); + } + kfree(usb); + return -ENOMEM; +} + +static void go7007_usb_disconnect(struct usb_interface *intf) +{ + struct go7007 *go = to_go7007(usb_get_intfdata(intf)); + struct go7007_usb *usb = go->hpi_context; + struct urb *vurb, *aurb; + int i; + + go->status = STATUS_SHUTDOWN; + usb_kill_urb(usb->intr_urb); + + /* Free USB-related structs */ + for (i = 0; i < 8; ++i) { + vurb = usb->video_urbs[i]; + if (vurb) { + usb_kill_urb(vurb); + kfree(vurb->transfer_buffer); + usb_free_urb(vurb); + } + aurb = usb->audio_urbs[i]; + if (aurb) { + usb_kill_urb(aurb); + kfree(aurb->transfer_buffer); + usb_free_urb(aurb); + } + } + kfree(usb->intr_urb->transfer_buffer); + usb_free_urb(usb->intr_urb); + + kfree(go->hpi_context); + + go7007_remove(go); +} + +static struct usb_driver go7007_usb_driver = { + .name = "go7007", + .probe = go7007_usb_probe, + .disconnect = go7007_usb_disconnect, + .id_table = go7007_usb_id_table, +}; + +static int __init go7007_usb_init(void) +{ + return usb_register(&go7007_usb_driver); +} + +static void __exit go7007_usb_cleanup(void) +{ + usb_deregister(&go7007_usb_driver); +} + +module_init(go7007_usb_init); +module_exit(go7007_usb_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/go7007-v4l2.c b/drivers/staging/media/go7007/go7007-v4l2.c new file mode 100644 index 000000000000..2b27d8da70a2 --- /dev/null +++ b/drivers/staging/media/go7007/go7007-v4l2.c @@ -0,0 +1,1839 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007.h" +#include "go7007-priv.h" +#include "wis-i2c.h" + +/* Temporary defines until accepted in v4l-dvb */ +#ifndef V4L2_MPEG_STREAM_TYPE_MPEG_ELEM +#define V4L2_MPEG_STREAM_TYPE_MPEG_ELEM 6 /* MPEG elementary stream */ +#endif +#ifndef V4L2_MPEG_VIDEO_ENCODING_MPEG_4 +#define V4L2_MPEG_VIDEO_ENCODING_MPEG_4 3 +#endif + +#define call_all(dev, o, f, args...) \ + v4l2_device_call_until_err(dev, 0, o, f, ##args) + +static void deactivate_buffer(struct go7007_buffer *gobuf) +{ + int i; + + if (gobuf->state != BUF_STATE_IDLE) { + list_del(&gobuf->stream); + gobuf->state = BUF_STATE_IDLE; + } + if (gobuf->page_count > 0) { + for (i = 0; i < gobuf->page_count; ++i) + page_cache_release(gobuf->pages[i]); + gobuf->page_count = 0; + } +} + +static void abort_queued(struct go7007 *go) +{ + struct go7007_buffer *gobuf, *next; + + list_for_each_entry_safe(gobuf, next, &go->stream, stream) { + deactivate_buffer(gobuf); + } +} + +static int go7007_streamoff(struct go7007 *go) +{ + int retval = -EINVAL; + unsigned long flags; + + mutex_lock(&go->hw_lock); + if (go->streaming) { + go->streaming = 0; + go7007_stream_stop(go); + spin_lock_irqsave(&go->spinlock, flags); + abort_queued(go); + spin_unlock_irqrestore(&go->spinlock, flags); + go7007_reset_encoder(go); + retval = 0; + } + mutex_unlock(&go->hw_lock); + return 0; +} + +static int go7007_open(struct file *file) +{ + struct go7007 *go = video_get_drvdata(video_devdata(file)); + struct go7007_file *gofh; + + if (go->status != STATUS_ONLINE) + return -EBUSY; + gofh = kmalloc(sizeof(struct go7007_file), GFP_KERNEL); + if (gofh == NULL) + return -ENOMEM; + ++go->ref_count; + gofh->go = go; + mutex_init(&gofh->lock); + gofh->buf_count = 0; + file->private_data = gofh; + return 0; +} + +static int go7007_release(struct file *file) +{ + struct go7007_file *gofh = file->private_data; + struct go7007 *go = gofh->go; + + if (gofh->buf_count > 0) { + go7007_streamoff(go); + go->in_use = 0; + kfree(gofh->bufs); + gofh->buf_count = 0; + } + kfree(gofh); + if (--go->ref_count == 0) + kfree(go); + file->private_data = NULL; + return 0; +} + +static u32 get_frame_type_flag(struct go7007_buffer *gobuf, int format) +{ + u8 *f = page_address(gobuf->pages[0]); + + switch (format) { + case GO7007_FORMAT_MJPEG: + return V4L2_BUF_FLAG_KEYFRAME; + case GO7007_FORMAT_MPEG4: + switch ((f[gobuf->frame_offset + 4] >> 6) & 0x3) { + case 0: + return V4L2_BUF_FLAG_KEYFRAME; + case 1: + return V4L2_BUF_FLAG_PFRAME; + case 2: + return V4L2_BUF_FLAG_BFRAME; + default: + return 0; + } + case GO7007_FORMAT_MPEG1: + case GO7007_FORMAT_MPEG2: + switch ((f[gobuf->frame_offset + 5] >> 3) & 0x7) { + case 1: + return V4L2_BUF_FLAG_KEYFRAME; + case 2: + return V4L2_BUF_FLAG_PFRAME; + case 3: + return V4L2_BUF_FLAG_BFRAME; + default: + return 0; + } + } + + return 0; +} + +static int set_capture_size(struct go7007 *go, struct v4l2_format *fmt, int try) +{ + int sensor_height = 0, sensor_width = 0; + int width, height, i; + + if (fmt != NULL && fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG && + fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG && + fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MPEG4) + return -EINVAL; + + switch (go->standard) { + case GO7007_STD_NTSC: + sensor_width = 720; + sensor_height = 480; + break; + case GO7007_STD_PAL: + sensor_width = 720; + sensor_height = 576; + break; + case GO7007_STD_OTHER: + sensor_width = go->board_info->sensor_width; + sensor_height = go->board_info->sensor_height; + break; + } + + if (fmt == NULL) { + width = sensor_width; + height = sensor_height; + } else if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { + if (fmt->fmt.pix.width > sensor_width) + width = sensor_width; + else if (fmt->fmt.pix.width < 144) + width = 144; + else + width = fmt->fmt.pix.width & ~0x0f; + + if (fmt->fmt.pix.height > sensor_height) + height = sensor_height; + else if (fmt->fmt.pix.height < 96) + height = 96; + else + height = fmt->fmt.pix.height & ~0x0f; + } else { + int requested_size = fmt->fmt.pix.width * fmt->fmt.pix.height; + int sensor_size = sensor_width * sensor_height; + + if (64 * requested_size < 9 * sensor_size) { + width = sensor_width / 4; + height = sensor_height / 4; + } else if (64 * requested_size < 36 * sensor_size) { + width = sensor_width / 2; + height = sensor_height / 2; + } else { + width = sensor_width; + height = sensor_height; + } + width &= ~0xf; + height &= ~0xf; + } + + if (fmt != NULL) { + u32 pixelformat = fmt->fmt.pix.pixelformat; + + memset(fmt, 0, sizeof(*fmt)); + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->fmt.pix.width = width; + fmt->fmt.pix.height = height; + fmt->fmt.pix.pixelformat = pixelformat; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* ?? */ + } + + if (try) + return 0; + + go->width = width; + go->height = height; + go->encoder_h_offset = go->board_info->sensor_h_offset; + go->encoder_v_offset = go->board_info->sensor_v_offset; + for (i = 0; i < 4; ++i) + go->modet[i].enable = 0; + for (i = 0; i < 1624; ++i) + go->modet_map[i] = 0; + + if (go->board_info->sensor_flags & GO7007_SENSOR_SCALING) { + struct v4l2_mbus_framefmt mbus_fmt; + + mbus_fmt.code = V4L2_MBUS_FMT_FIXED; + if (fmt != NULL) + mbus_fmt.width = fmt->fmt.pix.width; + else + mbus_fmt.width = width; + + if (height > sensor_height / 2) { + mbus_fmt.height = height / 2; + go->encoder_v_halve = 0; + } else { + mbus_fmt.height = height; + go->encoder_v_halve = 1; + } + call_all(&go->v4l2_dev, video, s_mbus_fmt, &mbus_fmt); + } else { + if (width <= sensor_width / 4) { + go->encoder_h_halve = 1; + go->encoder_v_halve = 1; + go->encoder_subsample = 1; + } else if (width <= sensor_width / 2) { + go->encoder_h_halve = 1; + go->encoder_v_halve = 1; + go->encoder_subsample = 0; + } else { + go->encoder_h_halve = 0; + go->encoder_v_halve = 0; + go->encoder_subsample = 0; + } + } + + if (fmt == NULL) + return 0; + + switch (fmt->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_MPEG: + if (go->format == GO7007_FORMAT_MPEG1 || + go->format == GO7007_FORMAT_MPEG2 || + go->format == GO7007_FORMAT_MPEG4) + break; + go->format = GO7007_FORMAT_MPEG1; + go->pali = 0; + go->aspect_ratio = GO7007_RATIO_1_1; + go->gop_size = go->sensor_framerate / 1000; + go->ipb = 0; + go->closed_gop = 1; + go->repeat_seqhead = 1; + go->seq_header_enable = 1; + go->gop_header_enable = 1; + go->dvd_mode = 0; + break; + /* Backwards compatibility only! */ + case V4L2_PIX_FMT_MPEG4: + if (go->format == GO7007_FORMAT_MPEG4) + break; + go->format = GO7007_FORMAT_MPEG4; + go->pali = 0xf5; + go->aspect_ratio = GO7007_RATIO_1_1; + go->gop_size = go->sensor_framerate / 1000; + go->ipb = 0; + go->closed_gop = 1; + go->repeat_seqhead = 1; + go->seq_header_enable = 1; + go->gop_header_enable = 1; + go->dvd_mode = 0; + break; + case V4L2_PIX_FMT_MJPEG: + go->format = GO7007_FORMAT_MJPEG; + go->pali = 0; + go->aspect_ratio = GO7007_RATIO_1_1; + go->gop_size = 0; + go->ipb = 0; + go->closed_gop = 0; + go->repeat_seqhead = 0; + go->seq_header_enable = 0; + go->gop_header_enable = 0; + go->dvd_mode = 0; + break; + } + return 0; +} + +#if 0 +static int clip_to_modet_map(struct go7007 *go, int region, + struct v4l2_clip *clip_list) +{ + struct v4l2_clip clip, *clip_ptr; + int x, y, mbnum; + + /* Check if coordinates are OK and if any macroblocks are already + * used by other regions (besides 0) */ + clip_ptr = clip_list; + while (clip_ptr) { + if (copy_from_user(&clip, clip_ptr, sizeof(clip))) + return -EFAULT; + if (clip.c.left < 0 || (clip.c.left & 0xF) || + clip.c.width <= 0 || (clip.c.width & 0xF)) + return -EINVAL; + if (clip.c.left + clip.c.width > go->width) + return -EINVAL; + if (clip.c.top < 0 || (clip.c.top & 0xF) || + clip.c.height <= 0 || (clip.c.height & 0xF)) + return -EINVAL; + if (clip.c.top + clip.c.height > go->height) + return -EINVAL; + for (y = 0; y < clip.c.height; y += 16) + for (x = 0; x < clip.c.width; x += 16) { + mbnum = (go->width >> 4) * + ((clip.c.top + y) >> 4) + + ((clip.c.left + x) >> 4); + if (go->modet_map[mbnum] != 0 && + go->modet_map[mbnum] != region) + return -EBUSY; + } + clip_ptr = clip.next; + } + + /* Clear old region macroblocks */ + for (mbnum = 0; mbnum < 1624; ++mbnum) + if (go->modet_map[mbnum] == region) + go->modet_map[mbnum] = 0; + + /* Claim macroblocks in this list */ + clip_ptr = clip_list; + while (clip_ptr) { + if (copy_from_user(&clip, clip_ptr, sizeof(clip))) + return -EFAULT; + for (y = 0; y < clip.c.height; y += 16) + for (x = 0; x < clip.c.width; x += 16) { + mbnum = (go->width >> 4) * + ((clip.c.top + y) >> 4) + + ((clip.c.left + x) >> 4); + go->modet_map[mbnum] = region; + } + clip_ptr = clip.next; + } + return 0; +} +#endif + +static int mpeg_query_ctrl(struct v4l2_queryctrl *ctrl) +{ + static const u32 mpeg_ctrls[] = { + V4L2_CID_MPEG_CLASS, + V4L2_CID_MPEG_STREAM_TYPE, + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_ASPECT, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + V4L2_CID_MPEG_VIDEO_GOP_CLOSURE, + V4L2_CID_MPEG_VIDEO_BITRATE, + 0 + }; + static const u32 *ctrl_classes[] = { + mpeg_ctrls, + NULL + }; + + ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id); + + switch (ctrl->id) { + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill(ctrl, 0, 0, 0, 0); + case V4L2_CID_MPEG_STREAM_TYPE: + return v4l2_ctrl_query_fill(ctrl, + V4L2_MPEG_STREAM_TYPE_MPEG2_DVD, + V4L2_MPEG_STREAM_TYPE_MPEG_ELEM, 1, + V4L2_MPEG_STREAM_TYPE_MPEG_ELEM); + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill(ctrl, + V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_2); + case V4L2_CID_MPEG_VIDEO_ASPECT: + return v4l2_ctrl_query_fill(ctrl, + V4L2_MPEG_VIDEO_ASPECT_1x1, + V4L2_MPEG_VIDEO_ASPECT_16x9, 1, + V4L2_MPEG_VIDEO_ASPECT_1x1); + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(ctrl, 0, 34, 1, 15); + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill(ctrl, 0, 1, 1, 0); + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(ctrl, + 64000, + 10000000, 1, + 1500000); + default: + return -EINVAL; + } + return 0; +} + +static int mpeg_s_ctrl(struct v4l2_control *ctrl, struct go7007 *go) +{ + /* pretty sure we can't change any of these while streaming */ + if (go->streaming) + return -EBUSY; + + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + switch (ctrl->value) { + case V4L2_MPEG_STREAM_TYPE_MPEG2_DVD: + go->format = GO7007_FORMAT_MPEG2; + go->bitrate = 9800000; + go->gop_size = 15; + go->pali = 0x48; + go->closed_gop = 1; + go->repeat_seqhead = 0; + go->seq_header_enable = 1; + go->gop_header_enable = 1; + go->dvd_mode = 1; + break; + case V4L2_MPEG_STREAM_TYPE_MPEG_ELEM: + /* todo: */ + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + switch (ctrl->value) { + case V4L2_MPEG_VIDEO_ENCODING_MPEG_1: + go->format = GO7007_FORMAT_MPEG1; + go->pali = 0; + break; + case V4L2_MPEG_VIDEO_ENCODING_MPEG_2: + go->format = GO7007_FORMAT_MPEG2; + /*if (mpeg->pali >> 24 == 2) + go->pali = mpeg->pali & 0xff; + else*/ + go->pali = 0x48; + break; + case V4L2_MPEG_VIDEO_ENCODING_MPEG_4: + go->format = GO7007_FORMAT_MPEG4; + /*if (mpeg->pali >> 24 == 4) + go->pali = mpeg->pali & 0xff; + else*/ + go->pali = 0xf5; + break; + default: + return -EINVAL; + } + go->gop_header_enable = + /*mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER + ? 0 :*/ 1; + /*if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER) + go->repeat_seqhead = 1; + else*/ + go->repeat_seqhead = 0; + go->dvd_mode = 0; + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + if (go->format == GO7007_FORMAT_MJPEG) + return -EINVAL; + switch (ctrl->value) { + case V4L2_MPEG_VIDEO_ASPECT_1x1: + go->aspect_ratio = GO7007_RATIO_1_1; + break; + case V4L2_MPEG_VIDEO_ASPECT_4x3: + go->aspect_ratio = GO7007_RATIO_4_3; + break; + case V4L2_MPEG_VIDEO_ASPECT_16x9: + go->aspect_ratio = GO7007_RATIO_16_9; + break; + case V4L2_MPEG_VIDEO_ASPECT_221x100: + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + if (ctrl->value < 0 || ctrl->value > 34) + return -EINVAL; + go->gop_size = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + if (ctrl->value != 0 && ctrl->value != 1) + return -EINVAL; + go->closed_gop = ctrl->value; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + /* Upper bound is kind of arbitrary here */ + if (ctrl->value < 64000 || ctrl->value > 10000000) + return -EINVAL; + go->bitrate = ctrl->value; + break; + default: + return -EINVAL; + } + return 0; +} + +static int mpeg_g_ctrl(struct v4l2_control *ctrl, struct go7007 *go) +{ + switch (ctrl->id) { + case V4L2_CID_MPEG_STREAM_TYPE: + if (go->dvd_mode) + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_DVD; + else + ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG_ELEM; + break; + case V4L2_CID_MPEG_VIDEO_ENCODING: + switch (go->format) { + case GO7007_FORMAT_MPEG1: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_1; + break; + case GO7007_FORMAT_MPEG2: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2; + break; + case GO7007_FORMAT_MPEG4: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_ASPECT: + switch (go->aspect_ratio) { + case GO7007_RATIO_1_1: + ctrl->value = V4L2_MPEG_VIDEO_ASPECT_1x1; + break; + case GO7007_RATIO_4_3: + ctrl->value = V4L2_MPEG_VIDEO_ASPECT_4x3; + break; + case GO7007_RATIO_16_9: + ctrl->value = V4L2_MPEG_VIDEO_ASPECT_16x9; + break; + default: + return -EINVAL; + } + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrl->value = go->gop_size; + break; + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + ctrl->value = go->closed_gop; + break; + case V4L2_CID_MPEG_VIDEO_BITRATE: + ctrl->value = go->bitrate; + break; + default: + return -EINVAL; + } + return 0; +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + strlcpy(cap->driver, "go7007", sizeof(cap->driver)); + strlcpy(cap->card, go->name, sizeof(cap->card)); +#if 0 + strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info)); +#endif + + cap->version = KERNEL_VERSION(0, 9, 8); + + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING; /* | V4L2_CAP_AUDIO; */ + + if (go->board_info->flags & GO7007_BOARD_HAS_TUNER) + cap->capabilities |= V4L2_CAP_TUNER; + + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + char *desc = NULL; + + switch (fmt->index) { + case 0: + fmt->pixelformat = V4L2_PIX_FMT_MJPEG; + desc = "Motion-JPEG"; + break; + case 1: + fmt->pixelformat = V4L2_PIX_FMT_MPEG; + desc = "MPEG1/MPEG2/MPEG4"; + break; + default: + return -EINVAL; + } + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->flags = V4L2_FMT_FLAG_COMPRESSED; + + strncpy(fmt->description, desc, sizeof(fmt->description)); + + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fmt->fmt.pix.width = go->width; + fmt->fmt.pix.height = go->height; + fmt->fmt.pix.pixelformat = (go->format == GO7007_FORMAT_MJPEG) ? + V4L2_PIX_FMT_MJPEG : V4L2_PIX_FMT_MPEG; + fmt->fmt.pix.field = V4L2_FIELD_NONE; + fmt->fmt.pix.bytesperline = 0; + fmt->fmt.pix.sizeimage = GO7007_BUF_SIZE; + fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + return set_capture_size(go, fmt, 1); +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *fmt) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (go->streaming) + return -EBUSY; + + return set_capture_size(go, fmt, 0); +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + int retval = -EBUSY; + unsigned int count, i; + + if (go->streaming) + return retval; + + if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + req->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + mutex_lock(&gofh->lock); + for (i = 0; i < gofh->buf_count; ++i) + if (gofh->bufs[i].mapped > 0) + goto unlock_and_return; + + mutex_lock(&go->hw_lock); + if (go->in_use > 0 && gofh->buf_count == 0) { + mutex_unlock(&go->hw_lock); + goto unlock_and_return; + } + + if (gofh->buf_count > 0) + kfree(gofh->bufs); + + retval = -ENOMEM; + count = req->count; + if (count > 0) { + if (count < 2) + count = 2; + if (count > 32) + count = 32; + + gofh->bufs = kcalloc(count, sizeof(struct go7007_buffer), + GFP_KERNEL); + + if (!gofh->bufs) { + mutex_unlock(&go->hw_lock); + goto unlock_and_return; + } + + for (i = 0; i < count; ++i) { + gofh->bufs[i].go = go; + gofh->bufs[i].index = i; + gofh->bufs[i].state = BUF_STATE_IDLE; + gofh->bufs[i].mapped = 0; + } + + go->in_use = 1; + } else { + go->in_use = 0; + } + + gofh->buf_count = count; + mutex_unlock(&go->hw_lock); + mutex_unlock(&gofh->lock); + + memset(req, 0, sizeof(*req)); + + req->count = count; + req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + req->memory = V4L2_MEMORY_MMAP; + + return 0; + +unlock_and_return: + mutex_unlock(&gofh->lock); + return retval; +} + +static int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct go7007_file *gofh = priv; + int retval = -EINVAL; + unsigned int index; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return retval; + + index = buf->index; + + mutex_lock(&gofh->lock); + if (index >= gofh->buf_count) + goto unlock_and_return; + + memset(buf, 0, sizeof(*buf)); + buf->index = index; + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + switch (gofh->bufs[index].state) { + case BUF_STATE_QUEUED: + buf->flags = V4L2_BUF_FLAG_QUEUED; + break; + case BUF_STATE_DONE: + buf->flags = V4L2_BUF_FLAG_DONE; + break; + default: + buf->flags = 0; + } + + if (gofh->bufs[index].mapped) + buf->flags |= V4L2_BUF_FLAG_MAPPED; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = index * GO7007_BUF_SIZE; + buf->length = GO7007_BUF_SIZE; + mutex_unlock(&gofh->lock); + + return 0; + +unlock_and_return: + mutex_unlock(&gofh->lock); + return retval; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct go7007_buffer *gobuf; + unsigned long flags; + int retval = -EINVAL; + int ret; + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || + buf->memory != V4L2_MEMORY_MMAP) + return retval; + + mutex_lock(&gofh->lock); + if (buf->index < 0 || buf->index >= gofh->buf_count) + goto unlock_and_return; + + gobuf = &gofh->bufs[buf->index]; + if (!gobuf->mapped) + goto unlock_and_return; + + retval = -EBUSY; + if (gobuf->state != BUF_STATE_IDLE) + goto unlock_and_return; + + /* offset will be 0 until we really support USERPTR streaming */ + gobuf->offset = gobuf->user_addr & ~PAGE_MASK; + gobuf->bytesused = 0; + gobuf->frame_offset = 0; + gobuf->modet_active = 0; + if (gobuf->offset > 0) + gobuf->page_count = GO7007_BUF_PAGES + 1; + else + gobuf->page_count = GO7007_BUF_PAGES; + + retval = -ENOMEM; + down_read(¤t->mm->mmap_sem); + ret = get_user_pages(current, current->mm, + gobuf->user_addr & PAGE_MASK, gobuf->page_count, + 1, 1, gobuf->pages, NULL); + up_read(¤t->mm->mmap_sem); + + if (ret != gobuf->page_count) { + int i; + for (i = 0; i < ret; ++i) + page_cache_release(gobuf->pages[i]); + gobuf->page_count = 0; + goto unlock_and_return; + } + + gobuf->state = BUF_STATE_QUEUED; + spin_lock_irqsave(&go->spinlock, flags); + list_add_tail(&gobuf->stream, &go->stream); + spin_unlock_irqrestore(&go->spinlock, flags); + mutex_unlock(&gofh->lock); + + return 0; + +unlock_and_return: + mutex_unlock(&gofh->lock); + return retval; +} + + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct go7007_buffer *gobuf; + int retval = -EINVAL; + unsigned long flags; + u32 frame_type_flag; + DEFINE_WAIT(wait); + + if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return retval; + if (buf->memory != V4L2_MEMORY_MMAP) + return retval; + + mutex_lock(&gofh->lock); + if (list_empty(&go->stream)) + goto unlock_and_return; + gobuf = list_entry(go->stream.next, + struct go7007_buffer, stream); + + retval = -EAGAIN; + if (gobuf->state != BUF_STATE_DONE && + !(file->f_flags & O_NONBLOCK)) { + for (;;) { + prepare_to_wait(&go->frame_waitq, &wait, + TASK_INTERRUPTIBLE); + if (gobuf->state == BUF_STATE_DONE) + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + finish_wait(&go->frame_waitq, &wait); + } + if (gobuf->state != BUF_STATE_DONE) + goto unlock_and_return; + + spin_lock_irqsave(&go->spinlock, flags); + deactivate_buffer(gobuf); + spin_unlock_irqrestore(&go->spinlock, flags); + frame_type_flag = get_frame_type_flag(gobuf, go->format); + gobuf->state = BUF_STATE_IDLE; + + memset(buf, 0, sizeof(*buf)); + buf->index = gobuf->index; + buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->bytesused = gobuf->bytesused; + buf->flags = V4L2_BUF_FLAG_MAPPED | frame_type_flag; + buf->field = V4L2_FIELD_NONE; + buf->timestamp = gobuf->timestamp; + buf->sequence = gobuf->seq; + buf->memory = V4L2_MEMORY_MMAP; + buf->m.offset = gobuf->index * GO7007_BUF_SIZE; + buf->length = GO7007_BUF_SIZE; + buf->reserved = gobuf->modet_active; + + mutex_unlock(&gofh->lock); + return 0; + +unlock_and_return: + mutex_unlock(&gofh->lock); + return retval; +} + +static int vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + int retval = 0; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&gofh->lock); + mutex_lock(&go->hw_lock); + + if (!go->streaming) { + go->streaming = 1; + go->next_seq = 0; + go->active_buf = NULL; + if (go7007_start_encoder(go) < 0) + retval = -EIO; + else + retval = 0; + } + mutex_unlock(&go->hw_lock); + mutex_unlock(&gofh->lock); + + return retval; +} + +static int vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + mutex_lock(&gofh->lock); + go7007_streamoff(go); + mutex_unlock(&gofh->lock); + + return 0; +} + +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *query) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + int id = query->id; + + if (0 == call_all(&go->v4l2_dev, core, queryctrl, query)) + return 0; + + query->id = id; + return mpeg_query_ctrl(query); +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (0 == call_all(&go->v4l2_dev, core, g_ctrl, ctrl)) + return 0; + + return mpeg_g_ctrl(ctrl, go); +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (0 == call_all(&go->v4l2_dev, core, s_ctrl, ctrl)) + return 0; + + return mpeg_s_ctrl(ctrl, go); +} + +static int vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + struct v4l2_fract timeperframe = { + .numerator = 1001 * go->fps_scale, + .denominator = go->sensor_framerate, + }; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + parm->parm.capture.capability |= V4L2_CAP_TIMEPERFRAME; + parm->parm.capture.timeperframe = timeperframe; + + return 0; +} + +static int vidioc_s_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + unsigned int n, d; + + if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (parm->parm.capture.capturemode != 0) + return -EINVAL; + + n = go->sensor_framerate * + parm->parm.capture.timeperframe.numerator; + d = 1001 * parm->parm.capture.timeperframe.denominator; + if (n != 0 && d != 0 && n > d) + go->fps_scale = (n + d/2) / d; + else + go->fps_scale = 1; + + return 0; +} + +/* VIDIOC_ENUMSTD on go7007 were used for enumberating the supported fps and + its resolution, when the device is not connected to TV. + This were an API abuse, probably used by the lack of specific IOCTL's to + enumberate it, by the time the driver were written. + + However, since kernel 2.6.19, two new ioctls (VIDIOC_ENUM_FRAMEINTERVALS + and VIDIOC_ENUM_FRAMESIZES) were added for this purpose. + + The two functions bellow implements the newer ioctls +*/ +static int vidioc_enum_framesizes(struct file *filp, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + /* Return -EINVAL, if it is a TV board */ + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || + (go->board_info->sensor_flags & GO7007_SENSOR_TV)) + return -EINVAL; + + if (fsize->index > 0) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = go->board_info->sensor_width; + fsize->discrete.height = go->board_info->sensor_height; + + return 0; +} + +static int vidioc_enum_frameintervals(struct file *filp, void *priv, + struct v4l2_frmivalenum *fival) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + /* Return -EINVAL, if it is a TV board */ + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) || + (go->board_info->sensor_flags & GO7007_SENSOR_TV)) + return -EINVAL; + + if (fival->index > 0) + return -EINVAL; + + fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fival->discrete.numerator = 1001; + fival->discrete.denominator = go->board_info->sensor_framerate; + + return 0; +} + +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + switch (go->standard) { + case GO7007_STD_NTSC: + *std = V4L2_STD_NTSC; + break; + case GO7007_STD_PAL: + *std = V4L2_STD_PAL; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (go->streaming) + return -EBUSY; + + if (!(go->board_info->sensor_flags & GO7007_SENSOR_TV) && *std != 0) + return -EINVAL; + + if (*std == 0) + return -EINVAL; + + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && + go->input == go->board_info->num_inputs - 1) { + if (!go->i2c_adapter_online) + return -EIO; + if (call_all(&go->v4l2_dev, core, s_std, *std) < 0) + return -EINVAL; + } + + if (*std & V4L2_STD_NTSC) { + go->standard = GO7007_STD_NTSC; + go->sensor_framerate = 30000; + } else if (*std & V4L2_STD_PAL) { + go->standard = GO7007_STD_PAL; + go->sensor_framerate = 25025; + } else if (*std & V4L2_STD_SECAM) { + go->standard = GO7007_STD_PAL; + go->sensor_framerate = 25025; + } else + return -EINVAL; + + call_all(&go->v4l2_dev, core, s_std, *std); + set_capture_size(go, NULL, 0); + + return 0; +} + +static int vidioc_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && + go->input == go->board_info->num_inputs - 1) { + if (!go->i2c_adapter_online) + return -EIO; + return call_all(&go->v4l2_dev, video, querystd, std); + } else if (go->board_info->sensor_flags & GO7007_SENSOR_TV) + *std = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM; + else + *std = 0; + + return 0; +} + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (inp->index >= go->board_info->num_inputs) + return -EINVAL; + + strncpy(inp->name, go->board_info->inputs[inp->index].name, + sizeof(inp->name)); + + /* If this board has a tuner, it will be the last input */ + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && + inp->index == go->board_info->num_inputs - 1) + inp->type = V4L2_INPUT_TYPE_TUNER; + else + inp->type = V4L2_INPUT_TYPE_CAMERA; + + inp->audioset = 0; + inp->tuner = 0; + if (go->board_info->sensor_flags & GO7007_SENSOR_TV) + inp->std = V4L2_STD_NTSC | V4L2_STD_PAL | + V4L2_STD_SECAM; + else + inp->std = 0; + + return 0; +} + + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *input) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + *input = go->input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int input) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (input >= go->board_info->num_inputs) + return -EINVAL; + if (go->streaming) + return -EBUSY; + + go->input = input; + + return call_all(&go->v4l2_dev, video, s_routing, input, 0, 0); +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (t->index != 0) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + return call_all(&go->v4l2_dev, tuner, g_tuner, t); +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (t->index != 0) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + switch (go->board_id) { + case GO7007_BOARDID_PX_TV402U_NA: + case GO7007_BOARDID_PX_TV402U_JP: + /* No selectable options currently */ + if (t->audmode != V4L2_TUNER_MODE_STEREO) + return -EINVAL; + break; + } + + return call_all(&go->v4l2_dev, tuner, s_tuner, t); +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + f->type = V4L2_TUNER_ANALOG_TV; + + return call_all(&go->v4l2_dev, tuner, g_frequency, f); +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + return call_all(&go->v4l2_dev, tuner, s_frequency, f); +} + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + /* These specify the raw input of the sensor */ + switch (go->standard) { + case GO7007_STD_NTSC: + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = 480; + cropcap->defrect.top = 0; + cropcap->defrect.left = 0; + cropcap->defrect.width = 720; + cropcap->defrect.height = 480; + break; + case GO7007_STD_PAL: + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = 576; + cropcap->defrect.top = 0; + cropcap->defrect.left = 0; + cropcap->defrect.width = 720; + cropcap->defrect.height = 576; + break; + case GO7007_STD_OTHER: + cropcap->bounds.top = 0; + cropcap->bounds.left = 0; + cropcap->bounds.width = go->board_info->sensor_width; + cropcap->bounds.height = go->board_info->sensor_height; + cropcap->defrect.top = 0; + cropcap->defrect.left = 0; + cropcap->defrect.width = go->board_info->sensor_width; + cropcap->defrect.height = go->board_info->sensor_height; + break; + } + + return 0; +} + +static int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct go7007 *go = ((struct go7007_file *) priv)->go; + + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + crop->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + /* These specify the raw input of the sensor */ + switch (go->standard) { + case GO7007_STD_NTSC: + crop->c.top = 0; + crop->c.left = 0; + crop->c.width = 720; + crop->c.height = 480; + break; + case GO7007_STD_PAL: + crop->c.top = 0; + crop->c.left = 0; + crop->c.width = 720; + crop->c.height = 576; + break; + case GO7007_STD_OTHER: + crop->c.top = 0; + crop->c.left = 0; + crop->c.width = go->board_info->sensor_width; + crop->c.height = go->board_info->sensor_height; + break; + } + + return 0; +} + +/* FIXME: vidioc_s_crop is not really implemented!!! + */ +static int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return 0; +} + +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *params) +{ + memset(params, 0, sizeof(*params)); + params->quality = 50; /* ?? */ + params->jpeg_markers = V4L2_JPEG_MARKER_DHT | + V4L2_JPEG_MARKER_DQT; + + return 0; +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *params) +{ + if (params->quality != 50 || + params->jpeg_markers != (V4L2_JPEG_MARKER_DHT | + V4L2_JPEG_MARKER_DQT)) + return -EINVAL; + + return 0; +} + +/* FIXME: + Those ioctls are private, and not needed, since several standard + extended controls already provide streaming control. + So, those ioctls should be converted into vidioc_g_ext_ctrls() + and vidioc_s_ext_ctrls() + */ + +#if 0 + /* Temporary ioctls for controlling compression characteristics */ + case GO7007IOC_S_BITRATE: + { + int *bitrate = arg; + + if (go->streaming) + return -EINVAL; + /* Upper bound is kind of arbitrary here */ + if (*bitrate < 64000 || *bitrate > 10000000) + return -EINVAL; + go->bitrate = *bitrate; + return 0; + } + case GO7007IOC_G_BITRATE: + { + int *bitrate = arg; + + *bitrate = go->bitrate; + return 0; + } + case GO7007IOC_S_COMP_PARAMS: + { + struct go7007_comp_params *comp = arg; + + if (go->format == GO7007_FORMAT_MJPEG) + return -EINVAL; + if (comp->gop_size > 0) + go->gop_size = comp->gop_size; + else + go->gop_size = go->sensor_framerate / 1000; + if (go->gop_size != 15) + go->dvd_mode = 0; + /*go->ipb = comp->max_b_frames > 0;*/ /* completely untested */ + if (go->board_info->sensor_flags & GO7007_SENSOR_TV) { + switch (comp->aspect_ratio) { + case GO7007_ASPECT_RATIO_4_3_NTSC: + case GO7007_ASPECT_RATIO_4_3_PAL: + go->aspect_ratio = GO7007_RATIO_4_3; + break; + case GO7007_ASPECT_RATIO_16_9_NTSC: + case GO7007_ASPECT_RATIO_16_9_PAL: + go->aspect_ratio = GO7007_RATIO_16_9; + break; + default: + go->aspect_ratio = GO7007_RATIO_1_1; + break; + } + } + if (comp->flags & GO7007_COMP_OMIT_SEQ_HEADER) { + go->dvd_mode = 0; + go->seq_header_enable = 0; + } else { + go->seq_header_enable = 1; + } + /* fall-through */ + } + case GO7007IOC_G_COMP_PARAMS: + { + struct go7007_comp_params *comp = arg; + + if (go->format == GO7007_FORMAT_MJPEG) + return -EINVAL; + memset(comp, 0, sizeof(*comp)); + comp->gop_size = go->gop_size; + comp->max_b_frames = go->ipb ? 2 : 0; + switch (go->aspect_ratio) { + case GO7007_RATIO_4_3: + if (go->standard == GO7007_STD_NTSC) + comp->aspect_ratio = + GO7007_ASPECT_RATIO_4_3_NTSC; + else + comp->aspect_ratio = + GO7007_ASPECT_RATIO_4_3_PAL; + break; + case GO7007_RATIO_16_9: + if (go->standard == GO7007_STD_NTSC) + comp->aspect_ratio = + GO7007_ASPECT_RATIO_16_9_NTSC; + else + comp->aspect_ratio = + GO7007_ASPECT_RATIO_16_9_PAL; + break; + default: + comp->aspect_ratio = GO7007_ASPECT_RATIO_1_1; + break; + } + if (go->closed_gop) + comp->flags |= GO7007_COMP_CLOSED_GOP; + if (!go->seq_header_enable) + comp->flags |= GO7007_COMP_OMIT_SEQ_HEADER; + return 0; + } + case GO7007IOC_S_MPEG_PARAMS: + { + struct go7007_mpeg_params *mpeg = arg; + + if (go->format != GO7007_FORMAT_MPEG1 && + go->format != GO7007_FORMAT_MPEG2 && + go->format != GO7007_FORMAT_MPEG4) + return -EINVAL; + + if (mpeg->flags & GO7007_MPEG_FORCE_DVD_MODE) { + go->format = GO7007_FORMAT_MPEG2; + go->bitrate = 9800000; + go->gop_size = 15; + go->pali = 0x48; + go->closed_gop = 1; + go->repeat_seqhead = 0; + go->seq_header_enable = 1; + go->gop_header_enable = 1; + go->dvd_mode = 1; + } else { + switch (mpeg->mpeg_video_standard) { + case GO7007_MPEG_VIDEO_MPEG1: + go->format = GO7007_FORMAT_MPEG1; + go->pali = 0; + break; + case GO7007_MPEG_VIDEO_MPEG2: + go->format = GO7007_FORMAT_MPEG2; + if (mpeg->pali >> 24 == 2) + go->pali = mpeg->pali & 0xff; + else + go->pali = 0x48; + break; + case GO7007_MPEG_VIDEO_MPEG4: + go->format = GO7007_FORMAT_MPEG4; + if (mpeg->pali >> 24 == 4) + go->pali = mpeg->pali & 0xff; + else + go->pali = 0xf5; + break; + default: + return -EINVAL; + } + go->gop_header_enable = + mpeg->flags & GO7007_MPEG_OMIT_GOP_HEADER + ? 0 : 1; + if (mpeg->flags & GO7007_MPEG_REPEAT_SEQHEADER) + go->repeat_seqhead = 1; + else + go->repeat_seqhead = 0; + go->dvd_mode = 0; + } + /* fall-through */ + } + case GO7007IOC_G_MPEG_PARAMS: + { + struct go7007_mpeg_params *mpeg = arg; + + memset(mpeg, 0, sizeof(*mpeg)); + switch (go->format) { + case GO7007_FORMAT_MPEG1: + mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG1; + mpeg->pali = 0; + break; + case GO7007_FORMAT_MPEG2: + mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG2; + mpeg->pali = GO7007_MPEG_PROFILE(2, go->pali); + break; + case GO7007_FORMAT_MPEG4: + mpeg->mpeg_video_standard = GO7007_MPEG_VIDEO_MPEG4; + mpeg->pali = GO7007_MPEG_PROFILE(4, go->pali); + break; + default: + return -EINVAL; + } + if (!go->gop_header_enable) + mpeg->flags |= GO7007_MPEG_OMIT_GOP_HEADER; + if (go->repeat_seqhead) + mpeg->flags |= GO7007_MPEG_REPEAT_SEQHEADER; + if (go->dvd_mode) + mpeg->flags |= GO7007_MPEG_FORCE_DVD_MODE; + return 0; + } + case GO7007IOC_S_MD_PARAMS: + { + struct go7007_md_params *mdp = arg; + + if (mdp->region > 3) + return -EINVAL; + if (mdp->trigger > 0) { + go->modet[mdp->region].pixel_threshold = + mdp->pixel_threshold >> 1; + go->modet[mdp->region].motion_threshold = + mdp->motion_threshold >> 1; + go->modet[mdp->region].mb_threshold = + mdp->trigger >> 1; + go->modet[mdp->region].enable = 1; + } else + go->modet[mdp->region].enable = 0; + /* fall-through */ + } + case GO7007IOC_G_MD_PARAMS: + { + struct go7007_md_params *mdp = arg; + int region = mdp->region; + + if (mdp->region > 3) + return -EINVAL; + memset(mdp, 0, sizeof(struct go7007_md_params)); + mdp->region = region; + if (!go->modet[region].enable) + return 0; + mdp->pixel_threshold = + (go->modet[region].pixel_threshold << 1) + 1; + mdp->motion_threshold = + (go->modet[region].motion_threshold << 1) + 1; + mdp->trigger = + (go->modet[region].mb_threshold << 1) + 1; + return 0; + } + case GO7007IOC_S_MD_REGION: + { + struct go7007_md_region *region = arg; + + if (region->region < 1 || region->region > 3) + return -EINVAL; + return clip_to_modet_map(go, region->region, region->clips); + } +#endif + +static ssize_t go7007_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static void go7007_vm_open(struct vm_area_struct *vma) +{ + struct go7007_buffer *gobuf = vma->vm_private_data; + + ++gobuf->mapped; +} + +static void go7007_vm_close(struct vm_area_struct *vma) +{ + struct go7007_buffer *gobuf = vma->vm_private_data; + unsigned long flags; + + if (--gobuf->mapped == 0) { + spin_lock_irqsave(&gobuf->go->spinlock, flags); + deactivate_buffer(gobuf); + spin_unlock_irqrestore(&gobuf->go->spinlock, flags); + } +} + +/* Copied from videobuf-dma-sg.c */ +static int go7007_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf) +{ + struct page *page; + + page = alloc_page(GFP_USER | __GFP_DMA32); + if (!page) + return VM_FAULT_OOM; + clear_user_highpage(page, (unsigned long)vmf->virtual_address); + vmf->page = page; + return 0; +} + +static struct vm_operations_struct go7007_vm_ops = { + .open = go7007_vm_open, + .close = go7007_vm_close, + .fault = go7007_vm_fault, +}; + +static int go7007_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct go7007_file *gofh = file->private_data; + unsigned int index; + + if (gofh->go->status != STATUS_ONLINE) + return -EIO; + if (!(vma->vm_flags & VM_SHARED)) + return -EINVAL; /* only support VM_SHARED mapping */ + if (vma->vm_end - vma->vm_start != GO7007_BUF_SIZE) + return -EINVAL; /* must map exactly one full buffer */ + mutex_lock(&gofh->lock); + index = vma->vm_pgoff / GO7007_BUF_PAGES; + if (index >= gofh->buf_count) { + mutex_unlock(&gofh->lock); + return -EINVAL; /* trying to map beyond requested buffers */ + } + if (index * GO7007_BUF_PAGES != vma->vm_pgoff) { + mutex_unlock(&gofh->lock); + return -EINVAL; /* offset is not aligned on buffer boundary */ + } + if (gofh->bufs[index].mapped > 0) { + mutex_unlock(&gofh->lock); + return -EBUSY; + } + gofh->bufs[index].mapped = 1; + gofh->bufs[index].user_addr = vma->vm_start; + vma->vm_ops = &go7007_vm_ops; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_flags &= ~VM_IO; + vma->vm_private_data = &gofh->bufs[index]; + mutex_unlock(&gofh->lock); + return 0; +} + +static unsigned int go7007_poll(struct file *file, poll_table *wait) +{ + struct go7007_file *gofh = file->private_data; + struct go7007_buffer *gobuf; + + if (list_empty(&gofh->go->stream)) + return POLLERR; + gobuf = list_entry(gofh->go->stream.next, struct go7007_buffer, stream); + poll_wait(file, &gofh->go->frame_waitq, wait); + if (gobuf->state == BUF_STATE_DONE) + return POLLIN | POLLRDNORM; + return 0; +} + +static void go7007_vfl_release(struct video_device *vfd) +{ + struct go7007 *go = video_get_drvdata(vfd); + + video_device_release(vfd); + if (--go->ref_count == 0) + kfree(go); +} + +static struct v4l2_file_operations go7007_fops = { + .owner = THIS_MODULE, + .open = go7007_open, + .release = go7007_release, + .ioctl = video_ioctl2, + .read = go7007_read, + .mmap = go7007_mmap, + .poll = go7007_poll, +}; + +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_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_g_std = vidioc_g_std, + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, + .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, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_enum_framesizes = vidioc_enum_framesizes, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, + .vidioc_cropcap = vidioc_cropcap, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, +}; + +static struct video_device go7007_template = { + .name = "go7007", + .fops = &go7007_fops, + .release = go7007_vfl_release, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = V4L2_STD_ALL, + .current_norm = V4L2_STD_NTSC, +}; + +int go7007_v4l2_init(struct go7007 *go) +{ + int rv; + + go->video_dev = video_device_alloc(); + if (go->video_dev == NULL) + return -ENOMEM; + *go->video_dev = go7007_template; + go->video_dev->parent = go->dev; + rv = video_register_device(go->video_dev, VFL_TYPE_GRABBER, -1); + if (rv < 0) { + video_device_release(go->video_dev); + go->video_dev = NULL; + return rv; + } + rv = v4l2_device_register(go->dev, &go->v4l2_dev); + if (rv < 0) { + video_device_release(go->video_dev); + go->video_dev = NULL; + return rv; + } + video_set_drvdata(go->video_dev, go); + ++go->ref_count; + printk(KERN_INFO "%s: registered device %s [v4l2]\n", + go->video_dev->name, video_device_node_name(go->video_dev)); + + return 0; +} + +void go7007_v4l2_remove(struct go7007 *go) +{ + unsigned long flags; + + mutex_lock(&go->hw_lock); + if (go->streaming) { + go->streaming = 0; + go7007_stream_stop(go); + spin_lock_irqsave(&go->spinlock, flags); + abort_queued(go); + spin_unlock_irqrestore(&go->spinlock, flags); + } + mutex_unlock(&go->hw_lock); + if (go->video_dev) + video_unregister_device(go->video_dev); + v4l2_device_unregister(&go->v4l2_dev); +} diff --git a/drivers/staging/media/go7007/go7007.h b/drivers/staging/media/go7007/go7007.h new file mode 100644 index 000000000000..7399c915a934 --- /dev/null +++ b/drivers/staging/media/go7007/go7007.h @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2006 Micronas USA Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and the associated README documentation file (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* DEPRECATED -- use V4L2_PIX_FMT_MPEG and then call GO7007IOC_S_MPEG_PARAMS + * to select between MPEG1, MPEG2, and MPEG4 */ +#define V4L2_PIX_FMT_MPEG4 v4l2_fourcc('M', 'P', 'G', '4') /* MPEG4 */ + +/* These will be replaced with a better interface + * soon, so don't get too attached to them */ +#define GO7007IOC_S_BITRATE _IOW('V', BASE_VIDIOC_PRIVATE + 0, int) +#define GO7007IOC_G_BITRATE _IOR('V', BASE_VIDIOC_PRIVATE + 1, int) + +enum go7007_aspect_ratio { + GO7007_ASPECT_RATIO_1_1 = 0, + GO7007_ASPECT_RATIO_4_3_NTSC = 1, + GO7007_ASPECT_RATIO_4_3_PAL = 2, + GO7007_ASPECT_RATIO_16_9_NTSC = 3, + GO7007_ASPECT_RATIO_16_9_PAL = 4, +}; + +/* Used to set generic compression parameters */ +struct go7007_comp_params { + __u32 gop_size; + __u32 max_b_frames; + enum go7007_aspect_ratio aspect_ratio; + __u32 flags; + __u32 reserved[8]; +}; + +#define GO7007_COMP_CLOSED_GOP 0x00000001 +#define GO7007_COMP_OMIT_SEQ_HEADER 0x00000002 + +enum go7007_mpeg_video_standard { + GO7007_MPEG_VIDEO_MPEG1 = 0, + GO7007_MPEG_VIDEO_MPEG2 = 1, + GO7007_MPEG_VIDEO_MPEG4 = 2, +}; + +/* Used to set parameters for V4L2_PIX_FMT_MPEG format */ +struct go7007_mpeg_params { + enum go7007_mpeg_video_standard mpeg_video_standard; + __u32 flags; + __u32 pali; + __u32 reserved[8]; +}; + +#define GO7007_MPEG_FORCE_DVD_MODE 0x00000001 +#define GO7007_MPEG_OMIT_GOP_HEADER 0x00000002 +#define GO7007_MPEG_REPEAT_SEQHEADER 0x00000004 + +#define GO7007_MPEG_PROFILE(format, pali) (((format)<<24)|(pali)) + +#define GO7007_MPEG2_PROFILE_MAIN_MAIN GO7007_MPEG_PROFILE(2, 0x48) + +#define GO7007_MPEG4_PROFILE_S_L0 GO7007_MPEG_PROFILE(4, 0x08) +#define GO7007_MPEG4_PROFILE_S_L1 GO7007_MPEG_PROFILE(4, 0x01) +#define GO7007_MPEG4_PROFILE_S_L2 GO7007_MPEG_PROFILE(4, 0x02) +#define GO7007_MPEG4_PROFILE_S_L3 GO7007_MPEG_PROFILE(4, 0x03) +#define GO7007_MPEG4_PROFILE_ARTS_L1 GO7007_MPEG_PROFILE(4, 0x91) +#define GO7007_MPEG4_PROFILE_ARTS_L2 GO7007_MPEG_PROFILE(4, 0x92) +#define GO7007_MPEG4_PROFILE_ARTS_L3 GO7007_MPEG_PROFILE(4, 0x93) +#define GO7007_MPEG4_PROFILE_ARTS_L4 GO7007_MPEG_PROFILE(4, 0x94) +#define GO7007_MPEG4_PROFILE_AS_L0 GO7007_MPEG_PROFILE(4, 0xf0) +#define GO7007_MPEG4_PROFILE_AS_L1 GO7007_MPEG_PROFILE(4, 0xf1) +#define GO7007_MPEG4_PROFILE_AS_L2 GO7007_MPEG_PROFILE(4, 0xf2) +#define GO7007_MPEG4_PROFILE_AS_L3 GO7007_MPEG_PROFILE(4, 0xf3) +#define GO7007_MPEG4_PROFILE_AS_L4 GO7007_MPEG_PROFILE(4, 0xf4) +#define GO7007_MPEG4_PROFILE_AS_L5 GO7007_MPEG_PROFILE(4, 0xf5) + +struct go7007_md_params { + __u16 region; + __u16 trigger; + __u16 pixel_threshold; + __u16 motion_threshold; + __u32 reserved[8]; +}; + +struct go7007_md_region { + __u16 region; + __u16 flags; + struct v4l2_clip *clips; + __u32 reserved[8]; +}; + +#define GO7007IOC_S_MPEG_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 2, \ + struct go7007_mpeg_params) +#define GO7007IOC_G_MPEG_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 3, \ + struct go7007_mpeg_params) +#define GO7007IOC_S_COMP_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 4, \ + struct go7007_comp_params) +#define GO7007IOC_G_COMP_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 5, \ + struct go7007_comp_params) +#define GO7007IOC_S_MD_PARAMS _IOWR('V', BASE_VIDIOC_PRIVATE + 6, \ + struct go7007_md_params) +#define GO7007IOC_G_MD_PARAMS _IOR('V', BASE_VIDIOC_PRIVATE + 7, \ + struct go7007_md_params) +#define GO7007IOC_S_MD_REGION _IOW('V', BASE_VIDIOC_PRIVATE + 8, \ + struct go7007_md_region) diff --git a/drivers/staging/media/go7007/go7007.txt b/drivers/staging/media/go7007/go7007.txt new file mode 100644 index 000000000000..9db1f3952fd2 --- /dev/null +++ b/drivers/staging/media/go7007/go7007.txt @@ -0,0 +1,481 @@ +This is a driver for the WIS GO7007SB multi-format video encoder. + +Pete Eberlein + +The driver was originally released under the GPL and is currently hosted at: +http://nikosapi.org/wiki/index.php/WIS_Go7007_Linux_driver +The go7007 firmware can be acquired from the package on the site above. + +I've modified the driver to support the following Video4Linux2 MPEG +controls, with acceptable values: + +V4L2_CID_MPEG_STREAM_TYPE V4L2_MPEG_STREAM_TYPE_MPEG2_DVD + V4L2_MPEG_STREAM_TYPE_MPEG_ELEM +V4L2_CID_MPEG_VIDEO_ENCODING V4L2_MPEG_VIDEO_ENCODING_MPEG_1 + V4L2_MPEG_VIDEO_ENCODING_MPEG_2 + V4L2_MPEG_VIDEO_ENCODING_MPEG_4 +V4L2_CID_MPEG_VIDEO_ASPECT V4L2_MPEG_VIDEO_ASPECT_1x1 + V4L2_MPEG_VIDEO_ASPECT_4x3 + V4L2_MPEG_VIDEO_ASPECT_16x9 +V4L2_CID_MPEG_VIDEO_GOP_SIZE integer +V4L2_CID_MPEG_VIDEO_BITRATE 64000 .. 10000000 + +These should be used instead of the non-standard GO7007 ioctls described +below. + + +The README files from the orignal package appear below: + +--------------------------------------------------------------------------- + WIS GO7007SB Public Linux Driver +--------------------------------------------------------------------------- + + +*** Please see the file RELEASE-NOTES for important last-minute updates *** + + + 0. OVERVIEW AND LICENSING/DISCLAIMER + + +This driver kit contains Linux drivers for the WIS GO7007SB multi-format +video encoder. Only kernel version 2.6.x is supported. The video stream +is available through the Video4Linux2 API and the audio stream is available +through the ALSA API (or the OSS emulation layer of the ALSA system). + +The files in kernel/ and hotplug/ are licensed under the GNU General Public +License Version 2 from the Free Software Foundation. A copy of the license +is included in the file COPYING. + +The example applications in apps/ and C header files in include/ are +licensed under a permissive license included in the source files which +allows copying, modification and redistribution for any purpose without +attribution. + +The firmware files included in the firmware/ directory may be freely +redistributed only in conjunction with this document; but modification, +tampering and reverse engineering are prohibited. + +MICRONAS USA, INC., MAKES NO WARRANTIES TO ANY PERSON OR ENTITY WITH +RESPECT TO THE SOFTWARE OR ANY DERIVATIVES THEREOF OR ANY SERVICES OR +LICENSES AND DISCLAIMS ALL IMPLIED WARRANTIES, INCLUDING WITHOUT LIMITATION +WARRANTIES OF MERCHANTABILITY, SUPPORT, AND FITNESS FOR A PARTICULAR +PURPOSE AND NON-INFRINGEMENT. + + + 1. SYSTEM REQUIREMENTS + + +This driver requires Linux kernel 2.6. Kernel 2.4 is not supported. Using +kernel 2.6.10 or later is recommended, as earlier kernels are known to have +unstable USB 2.0 support. + +A fully built kernel source tree must be available. Typically this will be +linked from "/lib/modules//build" for convenience. If this +link does not exist, an extra parameter will need to be passed to the +`make` command. + +All vendor-built kernels should already be configured properly. However, +for custom-built kernels, the following options need to be enabled in the +kernel as built-in or modules: + + CONFIG_HOTPLUG - Support for hot-pluggable devices + CONFIG_MODULES - Enable loadable module support + CONFIG_KMOD - Automatic kernel module loading + CONFIG_FW_LOADER - Hotplug firmware loading support + CONFIG_I2C - I2C support + CONFIG_VIDEO_DEV - Video For Linux + CONFIG_SOUND - Sound card support + CONFIG_SND - Advanced Linux Sound Architecture + CONFIG_USB - Support for Host-side USB + CONFIG_USB_DEVICEFS - USB device filesystem + CONFIG_USB_EHCI_HCD - EHCI HCD (USB 2.0) support + +Additionally, to use the example application, the following options need to +be enabled in the ALSA section: + + CONFIG_SND_MIXER_OSS - OSS Mixer API + CONFIG_SND_PCM_OSS - OSS PCM (digital audio) API + +The hotplug scripts, along with the fxload utility, must also be installed. +These scripts can be obtained from . +Hotplugging is used for loading firmware into the Cypruss EZ-USB chip using +fxload and for loading firmware into the driver using the firmware agent. + + + 2. COMPILING AND INSTALLING THE DRIVER + + +Most users should be able to compile the driver by simply running: + + $ make + +in the top-level directory of the driver kit. First the kernel modules +will be built, followed by the example applications. + +If the build system is unable to locate the kernel source tree for the +currently-running kernel, or if the module should be built for a kernel +other than the currently-running kernel, an additional parameter will need +to be passed to make to specify the appropriate kernel source directory: + + $ make KERNELSRC=/usr/src/linux-2.6.10-custom3 + +Once the compile completes, the driver and firmware files should be +installed by running: + + $ make install + +The kernel modules will be placed in "/lib/modules//extra" +and the firmware files will be placed in the appropriate hotplug firmware +directory, usually /lib/firmware. In addition, USB maps and scripts will +be placed in /etc/hotplug/usb to enable fxload to initialize the EZ-USB +control chip when the device is connected. + + + 3. PAL/SECAM TUNER CONFIGURATION (TV402U-EU only) + + +The PAL model of the Plextor ConvertX TV402U may require additional +configuration to correctly select the appropriate TV frequency band and +audio subchannel. + +Users with a device other than the Plextor ConvertX TV402U-EU should skip +this section. + +The wide variety of PAL TV systems used in Europe requires that additional +information about the local TV standards be passed to the driver in order +to properly tune TV channels. The two necessary parameters are (a) the PAL +TV band, and (b) the audio subchannel format in use. + +In many cases, the appropriate TV band selection is passed to the driver +from applications. However, in some cases, the application only specifies +that the driver should use PAL but not the specific information about the +appropriate TV band. To work around this issue, the correct TV band may be +specified in the "force_band" parameter to the wis-sony-tuner module: + + TV band force_band + ------- ---------- + PAL B/G B + PAL I I + PAL D/K D + SECAM L L + +If the "force_band" parameter is specified, the driver will ignore any TV +band specified by applications and will always use the band provided in the +module parameter. + +The other parameter that can be specified is the audio subchannel format. +There are several stereo audio carrier systems in use, including NICAM and +three varieties of A2. To receive audio broadcast on one of these stereo +carriers, the "force_mpx_mode" parameter must be specified to the +wis-sony-tuner module. + + TV band Audio subcarrier force_mpx_mode + ------- ---------------- -------------- + PAL B/G Mono (default) 1 + PAL B/G A2 2 + PAL B/G NICAM 3 + PAL I Mono (default) 4 + PAL I NICAM 5 + PAL D/K Mono (default) 6 + PAL D/K A2 (1) 7 + PAL D/K A2 (2) 8 + PAL D/K A2 (3) 9 + PAL D/K NICAM 10 + SECAM L Mono (default) 11 + SECAM L NICAM 12 + +If the "force_mpx_mode" parameter is not specified, the correct mono-only +mode will be chosen based on the TV band. However, the tuner will not +receive stereo audio or bilingual broadcasts correctly. + +To pass the "force_band" or "force_mpx_mode" parameters to the +wis-sony-tuner module, the following line must be added to the modprobe +configuration file, which varies from one Linux distribution to another. + + options wis-sony-tuner force_band=B force_mpx_mode=2 + +The above example would force the tuner to the PAL B/G TV band and receive +stereo audio broadcasts on the A2 carrier. + +To verify that the configuration has been placed in the correct location, +execute: + + $ modprobe -c | grep wis-sony-tuner + +If the configuration line appears, then modprobe will pass the parameters +correctly the next time the wis-sony-tuner module is loaded into the +kernel. + + + 4. TESTING THE DRIVER + + +Because few Linux applications are able to correctly capture from +Video4Linux2 devices with only compressed formats supported, the new driver +should be tested with the "gorecord" application in the apps/ directory. + +First connect a video source to the device, such as a DVD player or VCR. +This will be captured to a file for testing the driver. If an input source +is unavailable, a test file can still be captured, but the video will be +black and the audio will be silent. + +This application will auto-detect the V4L2 and ALSA/OSS device names of the +hardware and will record video and audio to an AVI file for a specified +number of seconds. For example: + + $ apps/gorecord -duration 60 capture.avi + +If this application does not successfully record an AVI file, the error +messages produced by gorecord and recorded in the system log (usually in +/var/log/messages) should provide information to help resolve the problem. + +Supplying no parameters to gorecord will cause it to probe the available +devices and exit. Use the -help flag for usage information. + + + 5. USING THE DRIVER + + +The V4L2 device implemented by the driver provides a standard compressed +format API, within the following criteria: + + * Applications that only support the original Video4Linux1 API will not + be able to communicate with this driver at all. + + * No raw video modes are supported, so applications like xawtv that + expect only uncompressed video will not function. + + * Supported compression formats are: Motion-JPEG, MPEG1, MPEG2 and MPEG4. + + * MPEG video formats are delivered as Video Elementary Streams only. + Program Stream (PS), Transport Stream (TS) and Packetized Elementary + Stream (PES) formats are not supported. + + * Video parameters such as format and input port may not be changed while + the encoder is active. + + * The audio capture device only functions when the video encoder is + actively capturing video. Attempts to read from the audio device when + the encoder is inactive will result in an I/O error. + + * The native format of the audio device is 48Khz 2-channel 16-bit + little-endian PCM, delivered through the ALSA system. No audio + compression is implemented in the hardware. ALSA may convert to other + uncompressed formats on the fly. + +The include/ directory contains a C header file describing non-standard +features of the GO7007SB encoder, which are described below: + + + GO7007IOC_S_COMP_PARAMS, GO7007IOC_G_COMP_PARAMS + + These ioctls are used to negotiate general compression parameters. + + To query the current parameters, call the GO7007IOC_G_COMP_PARAMS ioctl + with a pointer to a struct go7007_comp_params. If the driver is not + set to MPEG format, the EINVAL error code will be returned. + + To change the current parameters, initialize all fields of a struct + go7007_comp_params and call the GO7007_IOC_S_COMP_PARAMS ioctl with a + pointer to this structure. The driver will return the current + parameters with any necessary changes to conform to the limitations of + the hardware or current compression mode. Any or all fields can be set + to zero to request a reasonable default value. If the driver is not + set to MPEG format, the EINVAL error code will be returned. When I/O + is in progress, the EBUSY error code will be returned. + + Fields in struct go7007_comp_params: + + __u32 The maximum number of frames in each + gop_size Group Of Pictures; i.e. the maximum + number of frames minus one between + each key frame. + + __u32 The maximum number of sequential + max_b_frames bidirectionally-predicted frames. + (B-frames are not yet supported.) + + enum go7007_aspect_ratio The aspect ratio to be encoded in the + aspect_ratio meta-data of the compressed format. + + Choices are: + GO7007_ASPECT_RATIO_1_1 + GO7007_ASPECT_RATIO_4_3_NTSC + GO7007_ASPECT_RATIO_4_3_PAL + GO7007_ASPECT_RATIO_16_9_NTSC + GO7007_ASPECT_RATIO_16_9_PAL + + __u32 Bit-wise OR of control flags (below) + flags + + Flags in struct go7007_comp_params: + + GO7007_COMP_CLOSED_GOP Only produce self-contained GOPs, used + to produce streams appropriate for + random seeking. + + GO7007_COMP_OMIT_SEQ_HEADER Omit the stream sequence header. + + + GO7007IOC_S_MPEG_PARAMS, GO7007IOC_G_MPEG_PARAMS + + These ioctls are used to negotiate MPEG-specific stream parameters when + the pixelformat has been set to V4L2_PIX_FMT_MPEG. + + To query the current parameters, call the GO7007IOC_G_MPEG_PARAMS ioctl + with a pointer to a struct go7007_mpeg_params. If the driver is not + set to MPEG format, the EINVAL error code will be returned. + + To change the current parameters, initialize all fields of a struct + go7007_mpeg_params and call the GO7007_IOC_S_MPEG_PARAMS ioctl with a + pointer to this structure. The driver will return the current + parameters with any necessary changes to conform to the limitations of + the hardware or selected MPEG mode. Any or all fields can be set to + zero to request a reasonable default value. If the driver is not set + to MPEG format, the EINVAL error code will be returned. When I/O is in + progress, the EBUSY error code will be returned. + + Fields in struct go7007_mpeg_params: + + enum go7007_mpeg_video_standard + mpeg_video_standard The MPEG video standard in which to + compress the video. + + Choices are: + GO7007_MPEG_VIDEO_MPEG1 + GO7007_MPEG_VIDEO_MPEG2 + GO7007_MPEG_VIDEO_MPEG4 + + __u32 Bit-wise OR of control flags (below) + flags + + __u32 The profile and level indication to be + pali stored in the sequence header. This + is only used as an indicator to the + decoder, and does not affect the MPEG + features used in the video stream. + Not valid for MPEG1. + + Choices for MPEG2 are: + GO7007_MPEG2_PROFILE_MAIN_MAIN + + Choices for MPEG4 are: + GO7007_MPEG4_PROFILE_S_L0 + GO7007_MPEG4_PROFILE_S_L1 + GO7007_MPEG4_PROFILE_S_L2 + GO7007_MPEG4_PROFILE_S_L3 + GO7007_MPEG4_PROFILE_ARTS_L1 + GO7007_MPEG4_PROFILE_ARTS_L2 + GO7007_MPEG4_PROFILE_ARTS_L3 + GO7007_MPEG4_PROFILE_ARTS_L4 + GO7007_MPEG4_PROFILE_AS_L0 + GO7007_MPEG4_PROFILE_AS_L1 + GO7007_MPEG4_PROFILE_AS_L2 + GO7007_MPEG4_PROFILE_AS_L3 + GO7007_MPEG4_PROFILE_AS_L4 + GO7007_MPEG4_PROFILE_AS_L5 + + Flags in struct go7007_mpeg_params: + + GO7007_MPEG_FORCE_DVD_MODE Force all compression parameters and + bitrate control settings to comply + with DVD MPEG2 stream requirements. + This overrides most compression and + bitrate settings! + + GO7007_MPEG_OMIT_GOP_HEADER Omit the GOP header. + + GO7007_MPEG_REPEAT_SEQHEADER Repeat the MPEG sequence header at + the start of each GOP. + + + GO7007IOC_S_BITRATE, GO7007IOC_G_BITRATE + + These ioctls are used to set and query the target bitrate value for the + compressed video stream. The bitrate may be selected by storing the + target bits per second in an int and calling GO7007IOC_S_BITRATE with a + pointer to the int. The bitrate may be queried by calling + GO7007IOC_G_BITRATE with a pointer to an int where the current bitrate + will be stored. + + Note that this is the primary means of controlling the video quality + for all compression modes, including V4L2_PIX_FMT_MJPEG. The + VIDIOC_S_JPEGCOMP ioctl is not supported. + + +---------------------------------------------------------------------------- + Installing the WIS PCI Voyager Driver +--------------------------------------------------------------------------- + +The WIS PCI Voyager driver requires several patches to the Linux 2.6.11.x +kernel source tree before compiling the driver. These patches update the +in-kernel SAA7134 driver to the newest development version and patch bugs +in the TDA8290/TDA8275 tuner driver. + +The following patches must be downloaded from Gerd Knorr's website and +applied in the order listed: + + http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner + http://dl.bytesex.org/patches/2.6.11-2/i2c-tuner2 + http://dl.bytesex.org/patches/2.6.11-2/v4l2-api-mpeg + http://dl.bytesex.org/patches/2.6.11-2/saa7134-update + +The following patches are included with this SDK and can be applied in any +order: + + patches/2.6.11/saa7134-voyager.diff + patches/2.6.11/tda8275-newaddr.diff + patches/2.6.11/tda8290-ntsc.diff + +Check to make sure the CONFIG_VIDEO_SAA7134 option is enabled in the kernel +configuration, and build and install the kernel. + +After rebooting into the new kernel, the GO7007 driver can be compiled and +installed: + + $ make SAA7134_BUILD=y + $ make install + $ modprobe saa7134-go7007 + +There will be two V4L video devices associated with the PCI Voyager. The +first device (most likely /dev/video0) provides access to the raw video +capture mode of the SAA7133 device and is used to configure the source +video parameters and tune the TV tuner. This device can be used with xawtv +or other V4L(2) video software as a standard uncompressed device. + +The second device (most likely /dev/video1) provides access to the +compression functions of the GO7007. It can be tested using the gorecord +application in the apps/ directory of this SDK: + + $ apps/gorecord -vdevice /dev/video1 -noaudio test.avi + +Currently the frame resolution is fixed at 720x480 (NTSC) or 720x576 (PAL), +and the video standard must be specified to both the raw and the compressed +video devices (xawtv and gorecord, for example). + + +-------------------------------------------------------------------------- +RELEASE NOTES FOR WIS GO7007SB LINUX DRIVER +--------------------------------------------------------------------------- + +Last updated: 5 November 2005 + + - Release 0.9.7 includes new support for using udev to run fxload. The + install script should automatically detect whether the old hotplug + scripts or the new udev rules should be used. To force the use of + hotplug, run "make install USE_UDEV=n". To force the use of udev, run + "make install USE_UDEV=y". + + - Motion detection is supported but undocumented. Try the `modet` app + for a demonstration of how to use the facility. + + - Using USB2.0 devices such as the TV402U with USB1.1 HCDs or hubs can + cause buffer overruns and frame drops, even at low framerates, due to + inconsistency in the bitrate control mechanism. + + - On devices with an SAA7115, including the Plextor ConvertX, video height + values of 96, 128, 160, 192, 256, 320, and 384 do not work in NTSC mode. + All valid heights up to 512 work correctly in PAL mode. + + - The WIS Star Trek and PCI Voyager boards have no support yet for audio + or the TV tuner. diff --git a/drivers/staging/media/go7007/s2250-board.c b/drivers/staging/media/go7007/s2250-board.c new file mode 100644 index 000000000000..e7736a915530 --- /dev/null +++ b/drivers/staging/media/go7007/s2250-board.c @@ -0,0 +1,698 @@ +/* + * Copyright (C) 2008 Sensoray Company 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "go7007-priv.h" + +MODULE_DESCRIPTION("Sensoray 2250/2251 i2c v4l2 subdev driver"); +MODULE_LICENSE("GPL v2"); + +#define TLV320_ADDRESS 0x34 +#define VPX322_ADDR_ANALOGCONTROL1 0x02 +#define VPX322_ADDR_BRIGHTNESS0 0x0127 +#define VPX322_ADDR_BRIGHTNESS1 0x0131 +#define VPX322_ADDR_CONTRAST0 0x0128 +#define VPX322_ADDR_CONTRAST1 0x0132 +#define VPX322_ADDR_HUE 0x00dc +#define VPX322_ADDR_SAT 0x0030 + +struct go7007_usb_board { + unsigned int flags; + struct go7007_board_info main_info; +}; + +struct go7007_usb { + struct go7007_usb_board *board; + struct mutex i2c_lock; + struct usb_device *usbdev; + struct urb *video_urbs[8]; + struct urb *audio_urbs[8]; + struct urb *intr_urb; +}; + +static unsigned char aud_regs[] = { + 0x1e, 0x00, + 0x00, 0x17, + 0x02, 0x17, + 0x04, 0xf9, + 0x06, 0xf9, + 0x08, 0x02, + 0x0a, 0x00, + 0x0c, 0x00, + 0x0a, 0x00, + 0x0c, 0x00, + 0x0e, 0x02, + 0x10, 0x00, + 0x12, 0x01, + 0x00, 0x00, +}; + + +static unsigned char vid_regs[] = { + 0xF2, 0x0f, + 0xAA, 0x00, + 0xF8, 0xff, + 0x00, 0x00, +}; + +static u16 vid_regs_fp[] = { + 0x028, 0x067, + 0x120, 0x016, + 0x121, 0xcF2, + 0x122, 0x0F2, + 0x123, 0x00c, + 0x124, 0x2d0, + 0x125, 0x2e0, + 0x126, 0x004, + 0x128, 0x1E0, + 0x12A, 0x016, + 0x12B, 0x0F2, + 0x12C, 0x0F2, + 0x12D, 0x00c, + 0x12E, 0x2d0, + 0x12F, 0x2e0, + 0x130, 0x004, + 0x132, 0x1E0, + 0x140, 0x060, + 0x153, 0x00C, + 0x154, 0x200, + 0x150, 0x801, + 0x000, 0x000 +}; + +/* PAL specific values */ +static u16 vid_regs_fp_pal[] = +{ + 0x120, 0x017, + 0x121, 0xd22, + 0x122, 0x122, + 0x12A, 0x017, + 0x12B, 0x122, + 0x12C, 0x122, + 0x140, 0x060, + 0x000, 0x000, +}; + +struct s2250 { + struct v4l2_subdev sd; + v4l2_std_id std; + int input; + int brightness; + int contrast; + int saturation; + int hue; + int reg12b_val; + int audio_input; + struct i2c_client *audio; +}; + +static inline struct s2250 *to_state(struct v4l2_subdev *sd) +{ + return container_of(sd, struct s2250, sd); +} + +/* from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ +static int go7007_usb_vendor_request(struct go7007 *go, u16 request, + u16 value, u16 index, void *transfer_buffer, int length, int in) +{ + struct go7007_usb *usb = go->hpi_context; + int timeout = 5000; + + if (in) { + return usb_control_msg(usb->usbdev, + usb_rcvctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN, + value, index, transfer_buffer, length, timeout); + } else { + return usb_control_msg(usb->usbdev, + usb_sndctrlpipe(usb->usbdev, 0), request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, index, transfer_buffer, length, timeout); + } +} +/* end from go7007-usb.c which is Copyright (C) 2005-2006 Micronas USA Inc.*/ + +static int write_reg(struct i2c_client *client, u8 reg, u8 value) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb; + int rc; + int dev_addr = client->addr << 1; /* firmware wants 8-bit address */ + u8 *buf; + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + if (buf == NULL) + return -ENOMEM; + + usb = go->hpi_context; + if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { + printk(KERN_INFO "i2c lock failed\n"); + kfree(buf); + return -EINTR; + } + rc = go7007_usb_vendor_request(go, 0x55, dev_addr, + (reg<<8 | value), + buf, + 16, 1); + + mutex_unlock(&usb->i2c_lock); + kfree(buf); + return rc; +} + +static int write_reg_fp(struct i2c_client *client, u16 addr, u16 val) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb; + u8 *buf; + struct s2250 *dec = i2c_get_clientdata(client); + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; + + + + memset(buf, 0xcd, 6); + + usb = go->hpi_context; + if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { + printk(KERN_INFO "i2c lock failed\n"); + kfree(buf); + return -EINTR; + } + if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) { + kfree(buf); + return -EFAULT; + } + + mutex_unlock(&usb->i2c_lock); + if (buf[0] == 0) { + unsigned int subaddr, val_read; + + subaddr = (buf[4] << 8) + buf[5]; + val_read = (buf[2] << 8) + buf[3]; + kfree(buf); + if (val_read != val) { + printk(KERN_INFO "invalid fp write %x %x\n", + val_read, val); + return -EFAULT; + } + if (subaddr != addr) { + printk(KERN_INFO "invalid fp write addr %x %x\n", + subaddr, addr); + return -EFAULT; + } + } else { + kfree(buf); + return -EFAULT; + } + + /* save last 12b value */ + if (addr == 0x12b) + dec->reg12b_val = val; + + return 0; +} + +static int read_reg_fp(struct i2c_client *client, u16 addr, u16 *val) +{ + struct go7007 *go = i2c_get_adapdata(client->adapter); + struct go7007_usb *usb; + u8 *buf; + + if (go == NULL) + return -ENODEV; + + if (go->status == STATUS_SHUTDOWN) + return -EBUSY; + + buf = kzalloc(16, GFP_KERNEL); + + if (buf == NULL) + return -ENOMEM; + + + + memset(buf, 0xcd, 6); + usb = go->hpi_context; + if (mutex_lock_interruptible(&usb->i2c_lock) != 0) { + printk(KERN_INFO "i2c lock failed\n"); + kfree(buf); + return -EINTR; + } + if (go7007_usb_vendor_request(go, 0x58, addr, 0, buf, 16, 1) < 0) { + kfree(buf); + return -EFAULT; + } + mutex_unlock(&usb->i2c_lock); + + *val = (buf[0] << 8) | buf[1]; + kfree(buf); + + return 0; +} + + +static int write_regs(struct i2c_client *client, u8 *regs) +{ + int i; + + for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { + if (write_reg(client, regs[i], regs[i+1]) < 0) { + printk(KERN_INFO "s2250: failed\n"); + return -1; + } + } + return 0; +} + +static int write_regs_fp(struct i2c_client *client, u16 *regs) +{ + int i; + + for (i = 0; !((regs[i] == 0x00) && (regs[i+1] == 0x00)); i += 2) { + if (write_reg_fp(client, regs[i], regs[i+1]) < 0) { + printk(KERN_INFO "s2250: failed fp\n"); + return -1; + } + } + return 0; +} + + +/* ------------------------------------------------------------------------- */ + +static int s2250_s_video_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int vidsys; + + vidsys = (state->std == V4L2_STD_NTSC) ? 0x01 : 0x00; + if (input == 0) { + /* composite */ + write_reg_fp(client, 0x20, 0x020 | vidsys); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + } else if (input == 1) { + /* S-Video */ + write_reg_fp(client, 0x20, 0x040 | vidsys); + write_reg_fp(client, 0x21, 0x666); + write_reg_fp(client, 0x140, 0x060); + } else { + return -EINVAL; + } + state->input = input; + return 0; +} + +static int s2250_s_std(struct v4l2_subdev *sd, v4l2_std_id norm) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + u16 vidsource; + + vidsource = (state->input == 1) ? 0x040 : 0x020; + switch (norm) { + case V4L2_STD_NTSC: + write_regs_fp(client, vid_regs_fp); + write_reg_fp(client, 0x20, vidsource | 1); + break; + case V4L2_STD_PAL: + write_regs_fp(client, vid_regs_fp); + write_regs_fp(client, vid_regs_fp_pal); + write_reg_fp(client, 0x20, vidsource); + break; + default: + return -EINVAL; + } + state->std = norm; + return 0; +} + +static int s2250_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *query) +{ + switch (query->id) { + case V4L2_CID_BRIGHTNESS: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_CONTRAST: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_SATURATION: + return v4l2_ctrl_query_fill(query, 0, 100, 1, 50); + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(query, -50, 50, 1, 0); + default: + return -EINVAL; + } + return 0; +} + +static int s2250_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + int value1; + u16 oldvalue; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 100) + state->brightness = 100; + else if (ctrl->value < 0) + state->brightness = 0; + else + state->brightness = ctrl->value; + value1 = (state->brightness - 50) * 255 / 100; + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS0, + value1 | (oldvalue & ~0xff)); + read_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_BRIGHTNESS1, + value1 | (oldvalue & ~0xff)); + write_reg_fp(client, 0x140, 0x60); + break; + case V4L2_CID_CONTRAST: + if (ctrl->value > 100) + state->contrast = 100; + else if (ctrl->value < 0) + state->contrast = 0; + else + state->contrast = ctrl->value; + value1 = state->contrast * 0x40 / 100; + if (value1 > 0x3f) + value1 = 0x3f; /* max */ + read_reg_fp(client, VPX322_ADDR_CONTRAST0, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST0, + value1 | (oldvalue & ~0x3f)); + read_reg_fp(client, VPX322_ADDR_CONTRAST1, &oldvalue); + write_reg_fp(client, VPX322_ADDR_CONTRAST1, + value1 | (oldvalue & ~0x3f)); + write_reg_fp(client, 0x140, 0x60); + break; + case V4L2_CID_SATURATION: + if (ctrl->value > 100) + state->saturation = 100; + else if (ctrl->value < 0) + state->saturation = 0; + else + state->saturation = ctrl->value; + value1 = state->saturation * 4140 / 100; + if (value1 > 4094) + value1 = 4094; + write_reg_fp(client, VPX322_ADDR_SAT, value1); + break; + case V4L2_CID_HUE: + if (ctrl->value > 50) + state->hue = 50; + else if (ctrl->value < -50) + state->hue = -50; + else + state->hue = ctrl->value; + /* clamp the hue range */ + value1 = state->hue * 280 / 50; + write_reg_fp(client, VPX322_ADDR_HUE, value1); + break; + default: + return -EINVAL; + } + return 0; +} + +static int s2250_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct s2250 *state = to_state(sd); + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = state->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = state->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = state->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = state->hue; + break; + default: + return -EINVAL; + } + return 0; +} + +static int s2250_s_mbus_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *fmt) +{ + struct s2250 *state = to_state(sd); + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (fmt->height < 640) { + write_reg_fp(client, 0x12b, state->reg12b_val | 0x400); + write_reg_fp(client, 0x140, 0x060); + } else { + write_reg_fp(client, 0x12b, state->reg12b_val & ~0x400); + write_reg_fp(client, 0x140, 0x060); + } + return 0; +} + +static int s2250_s_audio_routing(struct v4l2_subdev *sd, u32 input, u32 output, + u32 config) +{ + struct s2250 *state = to_state(sd); + + switch (input) { + case 0: + write_reg(state->audio, 0x08, 0x02); /* Line In */ + break; + case 1: + write_reg(state->audio, 0x08, 0x04); /* Mic */ + break; + case 2: + write_reg(state->audio, 0x08, 0x05); /* Mic Boost */ + break; + default: + return -EINVAL; + } + state->audio_input = input; + return 0; +} + + +static int s2250_log_status(struct v4l2_subdev *sd) +{ + struct s2250 *state = to_state(sd); + + v4l2_info(sd, "Standard: %s\n", state->std == V4L2_STD_NTSC ? "NTSC" : + state->std == V4L2_STD_PAL ? "PAL" : + state->std == V4L2_STD_SECAM ? "SECAM" : + "unknown"); + v4l2_info(sd, "Input: %s\n", state->input == 0 ? "Composite" : + state->input == 1 ? "S-video" : + "error"); + v4l2_info(sd, "Brightness: %d\n", state->brightness); + v4l2_info(sd, "Contrast: %d\n", state->contrast); + v4l2_info(sd, "Saturation: %d\n", state->saturation); + v4l2_info(sd, "Hue: %d\n", state->hue); return 0; + v4l2_info(sd, "Audio input: %s\n", state->audio_input == 0 ? "Line In" : + state->audio_input == 1 ? "Mic" : + state->audio_input == 2 ? "Mic Boost" : + "error"); + return 0; +} + +/* --------------------------------------------------------------------------*/ + +static const struct v4l2_subdev_core_ops s2250_core_ops = { + .log_status = s2250_log_status, + .g_ctrl = s2250_g_ctrl, + .s_ctrl = s2250_s_ctrl, + .queryctrl = s2250_queryctrl, + .s_std = s2250_s_std, +}; + +static const struct v4l2_subdev_audio_ops s2250_audio_ops = { + .s_routing = s2250_s_audio_routing, +}; + +static const struct v4l2_subdev_video_ops s2250_video_ops = { + .s_routing = s2250_s_video_routing, + .s_mbus_fmt = s2250_s_mbus_fmt, +}; + +static const struct v4l2_subdev_ops s2250_ops = { + .core = &s2250_core_ops, + .audio = &s2250_audio_ops, + .video = &s2250_video_ops, +}; + +/* --------------------------------------------------------------------------*/ + +static int s2250_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_client *audio; + struct i2c_adapter *adapter = client->adapter; + struct s2250 *state; + struct v4l2_subdev *sd; + u8 *data; + struct go7007 *go = i2c_get_adapdata(adapter); + struct go7007_usb *usb = go->hpi_context; + + audio = i2c_new_dummy(adapter, TLV320_ADDRESS >> 1); + if (audio == NULL) + return -ENOMEM; + + state = kmalloc(sizeof(struct s2250), GFP_KERNEL); + if (state == NULL) { + i2c_unregister_device(audio); + return -ENOMEM; + } + + sd = &state->sd; + v4l2_i2c_subdev_init(sd, client, &s2250_ops); + + v4l2_info(sd, "initializing %s at address 0x%x on %s\n", + "Sensoray 2250/2251", client->addr, client->adapter->name); + + state->std = V4L2_STD_NTSC; + state->brightness = 50; + state->contrast = 50; + state->saturation = 50; + state->hue = 0; + state->audio = audio; + + /* initialize the audio */ + if (write_regs(audio, aud_regs) < 0) { + printk(KERN_ERR + "s2250: error initializing audio\n"); + i2c_unregister_device(audio); + kfree(state); + return 0; + } + + if (write_regs(client, vid_regs) < 0) { + printk(KERN_ERR + "s2250: error initializing decoder\n"); + i2c_unregister_device(audio); + kfree(state); + return 0; + } + if (write_regs_fp(client, vid_regs_fp) < 0) { + printk(KERN_ERR + "s2250: error initializing decoder\n"); + i2c_unregister_device(audio); + kfree(state); + return 0; + } + /* set default channel */ + /* composite */ + write_reg_fp(client, 0x20, 0x020 | 1); + write_reg_fp(client, 0x21, 0x662); + write_reg_fp(client, 0x140, 0x060); + + /* set default audio input */ + state->audio_input = 0; + write_reg(client, 0x08, 0x02); /* Line In */ + + if (mutex_lock_interruptible(&usb->i2c_lock) == 0) { + data = kzalloc(16, GFP_KERNEL); + if (data != NULL) { + int rc; + rc = go7007_usb_vendor_request(go, 0x41, 0, 0, + data, 16, 1); + if (rc > 0) { + u8 mask; + data[0] = 0; + mask = 1<<5; + data[0] &= ~mask; + data[1] |= mask; + go7007_usb_vendor_request(go, 0x40, 0, + (data[1]<<8) + + data[1], + data, 16, 0); + } + kfree(data); + } + mutex_unlock(&usb->i2c_lock); + } + + v4l2_info(sd, "initialized successfully\n"); + return 0; +} + +static int s2250_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 s2250_id[] = { + { "s2250", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, s2250_id); + +static struct i2c_driver s2250_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "s2250", + }, + .probe = s2250_probe, + .remove = s2250_remove, + .id_table = s2250_id, +}; + +static __init int init_s2250(void) +{ + return i2c_add_driver(&s2250_driver); +} + +static __exit void exit_s2250(void) +{ + i2c_del_driver(&s2250_driver); +} + +module_init(init_s2250); +module_exit(exit_s2250); diff --git a/drivers/staging/media/go7007/s2250-loader.c b/drivers/staging/media/go7007/s2250-loader.c new file mode 100644 index 000000000000..4e132519e253 --- /dev/null +++ b/drivers/staging/media/go7007/s2250-loader.c @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2008 Sensoray Company 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include + +#define S2250_LOADER_FIRMWARE "s2250_loader.fw" +#define S2250_FIRMWARE "s2250.fw" + +typedef struct device_extension_s { + struct kref kref; + int minor; + struct usb_device *usbdev; +} device_extension_t, *pdevice_extension_t; + +#define USB_s2250loader_MAJOR 240 +#define USB_s2250loader_MINOR_BASE 0 +#define MAX_DEVICES 256 + +static pdevice_extension_t s2250_dev_table[MAX_DEVICES]; +static DEFINE_MUTEX(s2250_dev_table_mutex); + +#define to_s2250loader_dev_common(d) container_of(d, device_extension_t, kref) +static void s2250loader_delete(struct kref *kref) +{ + pdevice_extension_t s = to_s2250loader_dev_common(kref); + s2250_dev_table[s->minor] = NULL; + kfree(s); +} + +static int s2250loader_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + int minor, ret; + pdevice_extension_t s = NULL; + const struct firmware *fw; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + if (!usbdev) { + printk(KERN_ERR "Enter s2250loader_probe failed\n"); + return -1; + } + printk(KERN_INFO "Enter s2250loader_probe 2.6 kernel\n"); + printk(KERN_INFO "vendor id 0x%x, device id 0x%x devnum:%d\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, + usbdev->devnum); + + if (usbdev->descriptor.bNumConfigurations != 1) { + printk(KERN_ERR "can't handle multiple config\n"); + return -1; + } + mutex_lock(&s2250_dev_table_mutex); + + for (minor = 0; minor < MAX_DEVICES; minor++) { + if (s2250_dev_table[minor] == NULL) + break; + } + + if (minor < 0 || minor >= MAX_DEVICES) { + printk(KERN_ERR "Invalid minor: %d\n", minor); + goto failed; + } + + /* Allocate dev data structure */ + s = kmalloc(sizeof(device_extension_t), GFP_KERNEL); + if (s == NULL) { + printk(KERN_ERR "Out of memory\n"); + goto failed; + } + s2250_dev_table[minor] = s; + + printk(KERN_INFO "s2250loader_probe: Device %d on Bus %d Minor %d\n", + usbdev->devnum, usbdev->bus->busnum, minor); + + memset(s, 0, sizeof(device_extension_t)); + s->usbdev = usbdev; + printk(KERN_INFO "loading 2250 loader\n"); + + kref_init(&(s->kref)); + + mutex_unlock(&s2250_dev_table_mutex); + + if (request_firmware(&fw, S2250_LOADER_FIRMWARE, &usbdev->dev)) { + printk(KERN_ERR + "s2250: unable to load firmware from file \"%s\"\n", + S2250_LOADER_FIRMWARE); + goto failed2; + } + ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + printk(KERN_ERR "loader download failed\n"); + goto failed2; + } + + if (request_firmware(&fw, S2250_FIRMWARE, &usbdev->dev)) { + printk(KERN_ERR + "s2250: unable to load firmware from file \"%s\"\n", + S2250_FIRMWARE); + goto failed2; + } + ret = usb_cypress_load_firmware(usbdev, fw, CYPRESS_FX2); + release_firmware(fw); + if (0 != ret) { + printk(KERN_ERR "firmware_s2250 download failed\n"); + goto failed2; + } + + usb_set_intfdata(interface, s); + return 0; + +failed: + mutex_unlock(&s2250_dev_table_mutex); +failed2: + if (s) + kref_put(&(s->kref), s2250loader_delete); + + printk(KERN_ERR "probe failed\n"); + return -1; +} + +static void s2250loader_disconnect(struct usb_interface *interface) +{ + pdevice_extension_t s; + printk(KERN_INFO "s2250: disconnect\n"); + s = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + kref_put(&(s->kref), s2250loader_delete); +} + +static const struct usb_device_id s2250loader_ids[] = { + {USB_DEVICE(0x1943, 0xa250)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, s2250loader_ids); + +static struct usb_driver s2250loader_driver = { + .name = "s2250-loader", + .probe = s2250loader_probe, + .disconnect = s2250loader_disconnect, + .id_table = s2250loader_ids, +}; + +static int __init s2250loader_init(void) +{ + int r; + unsigned i = 0; + + for (i = 0; i < MAX_DEVICES; i++) + s2250_dev_table[i] = NULL; + + r = usb_register(&s2250loader_driver); + if (r) { + printk(KERN_ERR "usb_register failed. Error number %d\n", r); + return -1; + } + + printk(KERN_INFO "s2250loader_init: driver registered\n"); + return 0; +} +module_init(s2250loader_init); + +static void __exit s2250loader_cleanup(void) +{ + printk(KERN_INFO "s2250loader_cleanup\n"); + usb_deregister(&s2250loader_driver); +} +module_exit(s2250loader_cleanup); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("firmware loader for Sensoray 2250/2251"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/s2250-loader.h b/drivers/staging/media/go7007/s2250-loader.h new file mode 100644 index 000000000000..b7c301af16cc --- /dev/null +++ b/drivers/staging/media/go7007/s2250-loader.h @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#ifndef _S2250_LOADER_H_ +#define _S2250_LOADER_H_ + +extern int s2250loader_init(void); +extern void s2250loader_cleanup(void); + +#endif diff --git a/drivers/staging/media/go7007/saa7134-go7007.c b/drivers/staging/media/go7007/saa7134-go7007.c new file mode 100644 index 000000000000..cf7c34a99459 --- /dev/null +++ b/drivers/staging/media/go7007/saa7134-go7007.c @@ -0,0 +1,532 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "saa7134-reg.h" +#include "saa7134.h" +#include "go7007-priv.h" + +#define GO7007_HPI_DEBUG + +enum hpi_address { + HPI_ADDR_VIDEO_BUFFER = 0xe4, + HPI_ADDR_INIT_BUFFER = 0xea, + HPI_ADDR_INTR_RET_VALUE = 0xee, + HPI_ADDR_INTR_RET_DATA = 0xec, + HPI_ADDR_INTR_STATUS = 0xf4, + HPI_ADDR_INTR_WR_PARAM = 0xf6, + HPI_ADDR_INTR_WR_INDEX = 0xf8, +}; + +enum gpio_command { + GPIO_COMMAND_RESET = 0x00, /* 000b */ + GPIO_COMMAND_REQ1 = 0x04, /* 001b */ + GPIO_COMMAND_WRITE = 0x20, /* 010b */ + GPIO_COMMAND_REQ2 = 0x24, /* 011b */ + GPIO_COMMAND_READ = 0x80, /* 100b */ + GPIO_COMMAND_VIDEO = 0x84, /* 101b */ + GPIO_COMMAND_IDLE = 0xA0, /* 110b */ + GPIO_COMMAND_ADDR = 0xA4, /* 111b */ +}; + +struct saa7134_go7007 { + struct saa7134_dev *dev; + u8 *top; + u8 *bottom; + dma_addr_t top_dma; + dma_addr_t bottom_dma; +}; + +static struct go7007_board_info board_voyager = { + .firmware = "go7007tv.bin", + .flags = 0, + .sensor_flags = GO7007_SENSOR_656 | + GO7007_SENSOR_VALID_ENABLE | + GO7007_SENSOR_TV | + GO7007_SENSOR_VBI, + .audio_flags = GO7007_AUDIO_I2S_MODE_1 | + GO7007_AUDIO_WORD_16, + .audio_rate = 48000, + .audio_bclk_div = 8, + .audio_main_div = 2, + .hpi_buffer_cap = 7, + .num_inputs = 1, + .inputs = { + { + .name = "SAA7134", + }, + }, +}; +MODULE_FIRMWARE("go7007tv.bin"); + +/********************* Driver for GPIO HPI interface *********************/ + +static int gpio_write(struct saa7134_dev *dev, u8 addr, u16 data) +{ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + /* Write HPI address */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Write low byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, data & 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Write high byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, data >> 8); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + return 0; +} + +static int gpio_read(struct saa7134_dev *dev, u8 addr, u16 *data) +{ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + /* Write HPI address */ + saa_writeb(SAA7134_GPIO_GPSTATUS0, addr); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); + + /* Read low byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + *data = saa_readb(SAA7134_GPIO_GPSTATUS0); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + /* Read high byte */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_READ); + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + *data |= saa_readb(SAA7134_GPIO_GPSTATUS0) << 8; + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + + return 0; +} + +static int saa7134_go7007_interface_reset(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + u32 status; + u16 intr_val, intr_data; + int count = 20; + + saa_clearb(SAA7134_TS_PARALLEL, 0x80); /* Disable TS interface */ + saa_writeb(SAA7134_GPIO_GPMODE2, 0xa4); + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_RESET); + msleep(1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); + msleep(10); + + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + + status = saa_readb(SAA7134_GPIO_GPSTATUS2); + /*printk(KERN_DEBUG "status is %s\n", status & 0x40 ? "OK" : "not OK"); */ + + /* enter command mode...(?) */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ1); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_REQ2); + + do { + saa_clearb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + saa_setb(SAA7134_GPIO_GPMODE3, SAA7134_GPIO_GPRESCAN); + status = saa_readb(SAA7134_GPIO_GPSTATUS2); + /*printk(KERN_INFO "gpio is %08x\n", saa_readl(SAA7134_GPIO_GPSTATUS0 >> 2)); */ + } while (--count > 0); + + /* Wait for an interrupt to indicate successful hardware reset */ + if (go7007_read_interrupt(go, &intr_val, &intr_data) < 0 || + (intr_val & ~0x1) != 0x55aa) { + printk(KERN_ERR + "saa7134-go7007: unable to reset the GO7007\n"); + return -1; + } + return 0; +} + +static int saa7134_go7007_write_interrupt(struct go7007 *go, int addr, int data) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + int i; + u16 status_reg; + +#ifdef GO7007_HPI_DEBUG + printk(KERN_DEBUG + "saa7134-go7007: WriteInterrupt: %04x %04x\n", addr, data); +#endif + + for (i = 0; i < 100; ++i) { + gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); + if (!(status_reg & 0x0010)) + break; + msleep(10); + } + if (i == 100) { + printk(KERN_ERR + "saa7134-go7007: device is hung, status reg = 0x%04x\n", + status_reg); + return -1; + } + gpio_write(dev, HPI_ADDR_INTR_WR_PARAM, data); + gpio_write(dev, HPI_ADDR_INTR_WR_INDEX, addr); + + return 0; +} + +static int saa7134_go7007_read_interrupt(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + /* XXX we need to wait if there is no interrupt available */ + go->interrupt_available = 1; + gpio_read(dev, HPI_ADDR_INTR_RET_VALUE, &go->interrupt_value); + gpio_read(dev, HPI_ADDR_INTR_RET_DATA, &go->interrupt_data); +#ifdef GO7007_HPI_DEBUG + printk(KERN_DEBUG "saa7134-go7007: ReadInterrupt: %04x %04x\n", + go->interrupt_value, go->interrupt_data); +#endif + return 0; +} + +static void saa7134_go7007_irq_ts_done(struct saa7134_dev *dev, + unsigned long status) +{ + struct go7007 *go = video_get_drvdata(dev->empress_dev); + struct saa7134_go7007 *saa = go->hpi_context; + + if (!go->streaming) + return; + if (0 != (status & 0x000f0000)) + printk(KERN_DEBUG "saa7134-go7007: irq: lost %ld\n", + (status >> 16) & 0x0f); + if (status & 0x100000) { + dma_sync_single_for_cpu(&dev->pci->dev, + saa->bottom_dma, PAGE_SIZE, DMA_FROM_DEVICE); + go7007_parse_video_stream(go, saa->bottom, PAGE_SIZE); + saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma)); + } else { + dma_sync_single_for_cpu(&dev->pci->dev, + saa->top_dma, PAGE_SIZE, DMA_FROM_DEVICE); + go7007_parse_video_stream(go, saa->top, PAGE_SIZE); + saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma)); + } +} + +static int saa7134_go7007_stream_start(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + saa->top_dma = dma_map_page(&dev->pci->dev, virt_to_page(saa->top), + 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (!saa->top_dma) + return -ENOMEM; + saa->bottom_dma = dma_map_page(&dev->pci->dev, + virt_to_page(saa->bottom), + 0, PAGE_SIZE, DMA_FROM_DEVICE); + if (!saa->bottom_dma) { + dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + return -ENOMEM; + } + + saa_writel(SAA7134_VIDEO_PORT_CTRL0 >> 2, 0xA300B000); + saa_writel(SAA7134_VIDEO_PORT_CTRL4 >> 2, 0x40000200); + + /* Set HPI interface for video */ + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_VIDEO_BUFFER); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPMODE0, 0x00); + + /* Enable TS interface */ + saa_writeb(SAA7134_TS_PARALLEL, 0xe6); + + /* Reset TS interface */ + saa_setb(SAA7134_TS_SERIAL1, 0x01); + saa_clearb(SAA7134_TS_SERIAL1, 0x01); + + /* Set up transfer block size */ + saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 128 - 1); + saa_writeb(SAA7134_TS_DMA0, (PAGE_SIZE >> 7) - 1); + saa_writeb(SAA7134_TS_DMA1, 0); + saa_writeb(SAA7134_TS_DMA2, 0); + + /* Enable video streaming mode */ + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_VIDEO); + + saa_writel(SAA7134_RS_BA1(5), cpu_to_le32(saa->top_dma)); + saa_writel(SAA7134_RS_BA2(5), cpu_to_le32(saa->bottom_dma)); + saa_writel(SAA7134_RS_PITCH(5), 128); + saa_writel(SAA7134_RS_CONTROL(5), SAA7134_RS_CONTROL_BURST_MAX); + + /* Enable TS FIFO */ + saa_setl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); + + /* Enable DMA IRQ */ + saa_setl(SAA7134_IRQ1, + SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); + + return 0; +} + +static int saa7134_go7007_stream_stop(struct go7007 *go) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev; + + if (!saa) + return -EINVAL; + dev = saa->dev; + if (!dev) + return -EINVAL; + + /* Shut down TS FIFO */ + saa_clearl(SAA7134_MAIN_CTRL, SAA7134_MAIN_CTRL_TE5); + + /* Disable DMA IRQ */ + saa_clearl(SAA7134_IRQ1, + SAA7134_IRQ1_INTE_RA2_1 | SAA7134_IRQ1_INTE_RA2_0); + + /* Disable TS interface */ + saa_clearb(SAA7134_TS_PARALLEL, 0x80); + + dma_unmap_page(&dev->pci->dev, saa->top_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + dma_unmap_page(&dev->pci->dev, saa->bottom_dma, PAGE_SIZE, + DMA_FROM_DEVICE); + + return 0; +} + +static int saa7134_go7007_send_firmware(struct go7007 *go, u8 *data, int len) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + u16 status_reg; + int i; + +#ifdef GO7007_HPI_DEBUG + printk(KERN_DEBUG "saa7134-go7007: DownloadBuffer " + "sending %d bytes\n", len); +#endif + + while (len > 0) { + i = len > 64 ? 64 : len; + saa_writeb(SAA7134_GPIO_GPMODE0, 0xff); + saa_writeb(SAA7134_GPIO_GPSTATUS0, HPI_ADDR_INIT_BUFFER); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_ADDR); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + while (i-- > 0) { + saa_writeb(SAA7134_GPIO_GPSTATUS0, *data); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_WRITE); + saa_writeb(SAA7134_GPIO_GPSTATUS2, GPIO_COMMAND_IDLE); + ++data; + --len; + } + for (i = 0; i < 100; ++i) { + gpio_read(dev, HPI_ADDR_INTR_STATUS, &status_reg); + if (!(status_reg & 0x0002)) + break; + } + if (i == 100) { + printk(KERN_ERR "saa7134-go7007: device is hung, " + "status reg = 0x%04x\n", status_reg); + return -1; + } + } + return 0; +} + +static int saa7134_go7007_send_command(struct go7007 *go, unsigned int cmd, + void *arg) +{ + struct saa7134_go7007 *saa = go->hpi_context; + struct saa7134_dev *dev = saa->dev; + + switch (cmd) { + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + return saa7134_s_std_internal(dev, NULL, std); + } + case VIDIOC_G_STD: + { + v4l2_std_id *std = arg; + *std = dev->tvnorm->id; + return 0; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) + return saa7134_queryctrl(NULL, NULL, ctrl); + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) + return saa7134_g_ctrl_internal(dev, NULL, ctrl); + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + if (V4L2_CTRL_ID2CLASS(ctrl->id) == V4L2_CTRL_CLASS_USER) + return saa7134_s_ctrl_internal(dev, NULL, ctrl); + } + } + return -EINVAL; + +} + +static struct go7007_hpi_ops saa7134_go7007_hpi_ops = { + .interface_reset = saa7134_go7007_interface_reset, + .write_interrupt = saa7134_go7007_write_interrupt, + .read_interrupt = saa7134_go7007_read_interrupt, + .stream_start = saa7134_go7007_stream_start, + .stream_stop = saa7134_go7007_stream_stop, + .send_firmware = saa7134_go7007_send_firmware, + .send_command = saa7134_go7007_send_command, +}; + +/********************* Add/remove functions *********************/ + +static int saa7134_go7007_init(struct saa7134_dev *dev) +{ + struct go7007 *go; + struct saa7134_go7007 *saa; + + printk(KERN_DEBUG "saa7134-go7007: probing new SAA713X board\n"); + + saa = kzalloc(sizeof(struct saa7134_go7007), GFP_KERNEL); + if (saa == NULL) + return -ENOMEM; + + /* Allocate a couple pages for receiving the compressed stream */ + saa->top = (u8 *)get_zeroed_page(GFP_KERNEL); + if (!saa->top) + goto allocfail; + saa->bottom = (u8 *)get_zeroed_page(GFP_KERNEL); + if (!saa->bottom) + goto allocfail; + + go = go7007_alloc(&board_voyager, &dev->pci->dev); + if (go == NULL) + goto allocfail; + go->board_id = GO7007_BOARDID_PCI_VOYAGER; + strncpy(go->name, saa7134_boards[dev->board].name, sizeof(go->name)); + go->hpi_ops = &saa7134_go7007_hpi_ops; + go->hpi_context = saa; + saa->dev = dev; + + /* Boot the GO7007 */ + if (go7007_boot_encoder(go, go->board_info->flags & + GO7007_BOARD_USE_ONBOARD_I2C) < 0) + goto initfail; + + /* Do any final GO7007 initialization, then register the + * V4L2 and ALSA interfaces */ + if (go7007_register_encoder(go) < 0) + goto initfail; + dev->empress_dev = go->video_dev; + video_set_drvdata(dev->empress_dev, go); + + go->status = STATUS_ONLINE; + return 0; + +initfail: + go->status = STATUS_SHUTDOWN; + return 0; + +allocfail: + if (saa->top) + free_page((unsigned long)saa->top); + if (saa->bottom) + free_page((unsigned long)saa->bottom); + kfree(saa); + return -ENOMEM; +} + +static int saa7134_go7007_fini(struct saa7134_dev *dev) +{ + struct go7007 *go; + struct saa7134_go7007 *saa; + + if (NULL == dev->empress_dev) + return 0; + + go = video_get_drvdata(dev->empress_dev); + saa = go->hpi_context; + go->status = STATUS_SHUTDOWN; + free_page((unsigned long)saa->top); + free_page((unsigned long)saa->bottom); + kfree(saa); + go7007_remove(go); + dev->empress_dev = NULL; + + return 0; +} + +static struct saa7134_mpeg_ops saa7134_go7007_ops = { + .type = SAA7134_MPEG_GO7007, + .init = saa7134_go7007_init, + .fini = saa7134_go7007_fini, + .irq_ts_done = saa7134_go7007_irq_ts_done, +}; + +static int __init saa7134_go7007_mod_init(void) +{ + return saa7134_ts_register(&saa7134_go7007_ops); +} + +static void __exit saa7134_go7007_mod_cleanup(void) +{ + saa7134_ts_unregister(&saa7134_go7007_ops); +} + +module_init(saa7134_go7007_mod_init); +module_exit(saa7134_go7007_mod_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/snd-go7007.c b/drivers/staging/media/go7007/snd-go7007.c new file mode 100644 index 000000000000..deac938d8505 --- /dev/null +++ b/drivers/staging/media/go7007/snd-go7007.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "go7007-priv.h" + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +module_param_array(id, charp, NULL, 0444); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for the go7007 audio driver"); +MODULE_PARM_DESC(id, "ID string for the go7007 audio driver"); +MODULE_PARM_DESC(enable, "Enable for the go7007 audio driver"); + +struct go7007_snd { + struct snd_card *card; + struct snd_pcm *pcm; + struct snd_pcm_substream *substream; + spinlock_t lock; + int w_idx; + int hw_ptr; + int avail; + int capturing; +}; + +static struct snd_pcm_hardware go7007_snd_capture_hw = { + .info = (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_48000, + .rate_min = 48000, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = (128*1024), + .period_bytes_min = 4096, + .period_bytes_max = (128*1024), + .periods_min = 1, + .periods_max = 32, +}; + +static void parse_audio_stream_data(struct go7007 *go, u8 *buf, int length) +{ + struct go7007_snd *gosnd = go->snd_context; + struct snd_pcm_runtime *runtime = gosnd->substream->runtime; + int frames = bytes_to_frames(runtime, length); + + spin_lock(&gosnd->lock); + gosnd->hw_ptr += frames; + if (gosnd->hw_ptr >= runtime->buffer_size) + gosnd->hw_ptr -= runtime->buffer_size; + gosnd->avail += frames; + spin_unlock(&gosnd->lock); + if (gosnd->w_idx + length > runtime->dma_bytes) { + int cpy = runtime->dma_bytes - gosnd->w_idx; + + memcpy(runtime->dma_area + gosnd->w_idx, buf, cpy); + length -= cpy; + buf += cpy; + gosnd->w_idx = 0; + } + memcpy(runtime->dma_area + gosnd->w_idx, buf, length); + gosnd->w_idx += length; + spin_lock(&gosnd->lock); + if (gosnd->avail < runtime->period_size) { + spin_unlock(&gosnd->lock); + return; + } + gosnd->avail -= runtime->period_size; + spin_unlock(&gosnd->lock); + if (gosnd->capturing) + snd_pcm_period_elapsed(gosnd->substream); +} + +static int go7007_snd_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + unsigned int bytes; + + bytes = params_buffer_bytes(hw_params); + if (substream->runtime->dma_bytes > 0) + vfree(substream->runtime->dma_area); + substream->runtime->dma_bytes = 0; + substream->runtime->dma_area = vmalloc(bytes); + if (substream->runtime->dma_area == NULL) + return -ENOMEM; + substream->runtime->dma_bytes = bytes; + go->audio_deliver = parse_audio_stream_data; + return 0; +} + +static int go7007_snd_hw_free(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + + go->audio_deliver = NULL; + if (substream->runtime->dma_bytes > 0) + vfree(substream->runtime->dma_area); + substream->runtime->dma_bytes = 0; + return 0; +} + +static int go7007_snd_capture_open(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + unsigned long flags; + int r; + + spin_lock_irqsave(&gosnd->lock, flags); + if (gosnd->substream == NULL) { + gosnd->substream = substream; + substream->runtime->hw = go7007_snd_capture_hw; + r = 0; + } else + r = -EBUSY; + spin_unlock_irqrestore(&gosnd->lock, flags); + return r; +} + +static int go7007_snd_capture_close(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + + gosnd->substream = NULL; + return 0; +} + +static int go7007_snd_pcm_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +static int go7007_snd_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + /* Just set a flag to indicate we should signal ALSA when + * sound comes in */ + gosnd->capturing = 1; + return 0; + case SNDRV_PCM_TRIGGER_STOP: + gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; + gosnd->capturing = 0; + return 0; + default: + return -EINVAL; + } +} + +static snd_pcm_uframes_t go7007_snd_pcm_pointer(struct snd_pcm_substream *substream) +{ + struct go7007 *go = snd_pcm_substream_chip(substream); + struct go7007_snd *gosnd = go->snd_context; + + return gosnd->hw_ptr; +} + +static struct page *go7007_snd_pcm_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + return vmalloc_to_page(substream->runtime->dma_area + offset); +} + +static struct snd_pcm_ops go7007_snd_capture_ops = { + .open = go7007_snd_capture_open, + .close = go7007_snd_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = go7007_snd_hw_params, + .hw_free = go7007_snd_hw_free, + .prepare = go7007_snd_pcm_prepare, + .trigger = go7007_snd_pcm_trigger, + .pointer = go7007_snd_pcm_pointer, + .page = go7007_snd_pcm_page, +}; + +static int go7007_snd_free(struct snd_device *device) +{ + struct go7007 *go = device->device_data; + + kfree(go->snd_context); + go->snd_context = NULL; + if (--go->ref_count == 0) + kfree(go); + return 0; +} + +static struct snd_device_ops go7007_snd_device_ops = { + .dev_free = go7007_snd_free, +}; + +int go7007_snd_init(struct go7007 *go) +{ + static int dev; + struct go7007_snd *gosnd; + int ret = 0; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + gosnd = kmalloc(sizeof(struct go7007_snd), GFP_KERNEL); + if (gosnd == NULL) + return -ENOMEM; + spin_lock_init(&gosnd->lock); + gosnd->hw_ptr = gosnd->w_idx = gosnd->avail = 0; + gosnd->capturing = 0; + ret = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, + &gosnd->card); + if (ret < 0) { + kfree(gosnd); + return ret; + } + ret = snd_device_new(gosnd->card, SNDRV_DEV_LOWLEVEL, go, + &go7007_snd_device_ops); + if (ret < 0) { + kfree(gosnd); + return ret; + } + snd_card_set_dev(gosnd->card, go->dev); + ret = snd_pcm_new(gosnd->card, "go7007", 0, 0, 1, &gosnd->pcm); + if (ret < 0) { + snd_card_free(gosnd->card); + kfree(gosnd); + return ret; + } + strncpy(gosnd->card->driver, "go7007", sizeof(gosnd->card->driver)); + strncpy(gosnd->card->shortname, go->name, sizeof(gosnd->card->driver)); + strncpy(gosnd->card->longname, gosnd->card->shortname, + sizeof(gosnd->card->longname)); + + gosnd->pcm->private_data = go; + snd_pcm_set_ops(gosnd->pcm, SNDRV_PCM_STREAM_CAPTURE, + &go7007_snd_capture_ops); + + ret = snd_card_register(gosnd->card); + if (ret < 0) { + snd_card_free(gosnd->card); + kfree(gosnd); + return ret; + } + + gosnd->substream = NULL; + go->snd_context = gosnd; + ++dev; + ++go->ref_count; + + return 0; +} +EXPORT_SYMBOL(go7007_snd_init); + +int go7007_snd_remove(struct go7007 *go) +{ + struct go7007_snd *gosnd = go->snd_context; + + snd_card_disconnect(gosnd->card); + snd_card_free_when_closed(gosnd->card); + return 0; +} +EXPORT_SYMBOL(go7007_snd_remove); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-i2c.h b/drivers/staging/media/go7007/wis-i2c.h new file mode 100644 index 000000000000..3c2b9be455df --- /dev/null +++ b/drivers/staging/media/go7007/wis-i2c.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +/* Temporary I2C IDs -- these need to be replaced with real registered IDs */ +#define I2C_DRIVERID_WIS_SAA7115 0xf0f0 +#define I2C_DRIVERID_WIS_UDA1342 0xf0f1 +#define I2C_DRIVERID_WIS_SONY_TUNER 0xf0f2 +#define I2C_DRIVERID_WIS_TW9903 0xf0f3 +#define I2C_DRIVERID_WIS_SAA7113 0xf0f4 +#define I2C_DRIVERID_WIS_OV7640 0xf0f5 +#define I2C_DRIVERID_WIS_TW2804 0xf0f6 +#define I2C_DRIVERID_S2250 0xf0f7 + +/* Flag to indicate that the client needs to be accessed with SCCB semantics */ +/* We re-use the I2C_M_TEN value so the flag passes through the masks in the + * core I2C code. Major kludge, but the I2C layer ain't exactly flexible. */ +#define I2C_CLIENT_SCCB 0x10 + +/* Definitions for new video decoder commands */ + +struct video_decoder_resolution { + unsigned int width; + unsigned int height; +}; + +#define DECODER_SET_RESOLUTION _IOW('d', 200, struct video_decoder_resolution) +#define DECODER_SET_CHANNEL _IOW('d', 201, int) + +/* Sony tuner types */ + +#define TUNER_SONY_BTF_PG472Z 200 +#define TUNER_SONY_BTF_PK467Z 201 +#define TUNER_SONY_BTF_PB463Z 202 diff --git a/drivers/staging/media/go7007/wis-ov7640.c b/drivers/staging/media/go7007/wis-ov7640.c new file mode 100644 index 000000000000..6bc9470fecb6 --- /dev/null +++ b/drivers/staging/media/go7007/wis-ov7640.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include + +#include "wis-i2c.h" + +struct wis_ov7640 { + int brightness; + int contrast; + int saturation; + int hue; +}; + +static u8 initial_registers[] = +{ + 0x12, 0x80, + 0x12, 0x54, + 0x14, 0x24, + 0x15, 0x01, + 0x28, 0x20, + 0x75, 0x82, + 0xFF, 0xFF, /* Terminator (reg 0xFF is unused) */ +}; + +static int write_regs(struct i2c_client *client, u8 *regs) +{ + int i; + + for (i = 0; regs[i] != 0xFF; i += 2) + if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) + return -1; + return 0; +} + +static int wis_ov7640_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + client->flags = I2C_CLIENT_SCCB; + + printk(KERN_DEBUG + "wis-ov7640: initializing OV7640 at address %d on %s\n", + client->addr, adapter->name); + + if (write_regs(client, initial_registers) < 0) { + printk(KERN_ERR "wis-ov7640: error initializing OV7640\n"); + return -ENODEV; + } + + return 0; +} + +static int wis_ov7640_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id wis_ov7640_id[] = { + { "wis_ov7640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wis_ov7640_id); + +static struct i2c_driver wis_ov7640_driver = { + .driver = { + .name = "WIS OV7640 I2C driver", + }, + .probe = wis_ov7640_probe, + .remove = wis_ov7640_remove, + .id_table = wis_ov7640_id, +}; + +static int __init wis_ov7640_init(void) +{ + return i2c_add_driver(&wis_ov7640_driver); +} + +static void __exit wis_ov7640_cleanup(void) +{ + i2c_del_driver(&wis_ov7640_driver); +} + +module_init(wis_ov7640_init); +module_exit(wis_ov7640_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-saa7113.c b/drivers/staging/media/go7007/wis-saa7113.c new file mode 100644 index 000000000000..05e0e1083864 --- /dev/null +++ b/drivers/staging/media/go7007/wis-saa7113.c @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "wis-i2c.h" + +struct wis_saa7113 { + int norm; + int brightness; + int contrast; + int saturation; + int hue; +}; + +static u8 initial_registers[] = +{ + 0x01, 0x08, + 0x02, 0xc0, + 0x03, 0x33, + 0x04, 0x00, + 0x05, 0x00, + 0x06, 0xe9, + 0x07, 0x0d, + 0x08, 0xd8, + 0x09, 0x40, + 0x0a, 0x80, + 0x0b, 0x47, + 0x0c, 0x40, + 0x0d, 0x00, + 0x0e, 0x01, + 0x0f, 0x2a, + 0x10, 0x40, + 0x11, 0x0c, + 0x12, 0xfe, + 0x13, 0x00, + 0x14, 0x00, + 0x15, 0x04, + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0x00, + 0x19, 0x00, + 0x1a, 0x00, + 0x1b, 0x00, + 0x1c, 0x00, + 0x1d, 0x00, + 0x1e, 0x00, + 0x1f, 0xc8, + 0x40, 0x00, + 0x41, 0xff, + 0x42, 0xff, + 0x43, 0xff, + 0x44, 0xff, + 0x45, 0xff, + 0x46, 0xff, + 0x47, 0xff, + 0x48, 0xff, + 0x49, 0xff, + 0x4a, 0xff, + 0x4b, 0xff, + 0x4c, 0xff, + 0x4d, 0xff, + 0x4e, 0xff, + 0x4f, 0xff, + 0x50, 0xff, + 0x51, 0xff, + 0x52, 0xff, + 0x53, 0xff, + 0x54, 0xff, + 0x55, 0xff, + 0x56, 0xff, + 0x57, 0xff, + 0x58, 0x00, + 0x59, 0x54, + 0x5a, 0x07, + 0x5b, 0x83, + 0x5c, 0x00, + 0x5d, 0x00, + 0x5e, 0x00, + 0x5f, 0x00, + 0x60, 0x00, + 0x61, 0x00, + 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ +}; + +static int write_reg(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int write_regs(struct i2c_client *client, u8 *regs) +{ + int i; + + for (i = 0; regs[i] != 0x00; i += 2) + if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) + return -1; + return 0; +} + +static int wis_saa7113_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct wis_saa7113 *dec = i2c_get_clientdata(client); + + switch (cmd) { + case VIDIOC_S_INPUT: + { + int *input = arg; + + i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input); + i2c_smbus_write_byte_data(client, 0x09, + *input < 6 ? 0x40 : 0x80); + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *input = arg; + dec->norm = *input; + if (dec->norm & V4L2_STD_NTSC) { + write_reg(client, 0x0e, 0x01); + write_reg(client, 0x10, 0x40); + } else if (dec->norm & V4L2_STD_PAL) { + write_reg(client, 0x0e, 0x01); + write_reg(client, 0x10, 0x48); + } else if (dec->norm * V4L2_STD_SECAM) { + write_reg(client, 0x0e, 0x50); + write_reg(client, 0x10, 0x48); + } + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_CONTRAST: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 71; + ctrl->flags = 0; + break; + case V4L2_CID_SATURATION: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 64; + ctrl->flags = 0; + break; + case V4L2_CID_HUE: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); + ctrl->minimum = -128; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 0; + ctrl->flags = 0; + break; + } + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 255) + dec->brightness = 255; + else if (ctrl->value < 0) + dec->brightness = 0; + else + dec->brightness = ctrl->value; + write_reg(client, 0x0a, dec->brightness); + break; + case V4L2_CID_CONTRAST: + if (ctrl->value > 127) + dec->contrast = 127; + else if (ctrl->value < 0) + dec->contrast = 0; + else + dec->contrast = ctrl->value; + write_reg(client, 0x0b, dec->contrast); + break; + case V4L2_CID_SATURATION: + if (ctrl->value > 127) + dec->saturation = 127; + else if (ctrl->value < 0) + dec->saturation = 0; + else + dec->saturation = ctrl->value; + write_reg(client, 0x0c, dec->saturation); + break; + case V4L2_CID_HUE: + if (ctrl->value > 127) + dec->hue = 127; + else if (ctrl->value < -128) + dec->hue = -128; + else + dec->hue = ctrl->value; + write_reg(client, 0x0d, dec->hue); + break; + } + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dec->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dec->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dec->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dec->hue; + break; + } + break; + } + default: + break; + } + return 0; +} + +static int wis_saa7113_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct wis_saa7113 *dec; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + dec = kmalloc(sizeof(struct wis_saa7113), GFP_KERNEL); + if (dec == NULL) + return -ENOMEM; + + dec->norm = V4L2_STD_NTSC; + dec->brightness = 128; + dec->contrast = 71; + dec->saturation = 64; + dec->hue = 0; + i2c_set_clientdata(client, dec); + + printk(KERN_DEBUG + "wis-saa7113: initializing SAA7113 at address %d on %s\n", + client->addr, adapter->name); + + if (write_regs(client, initial_registers) < 0) { + printk(KERN_ERR + "wis-saa7113: error initializing SAA7113\n"); + kfree(dec); + return -ENODEV; + } + + return 0; +} + +static int wis_saa7113_remove(struct i2c_client *client) +{ + struct wis_saa7113 *dec = i2c_get_clientdata(client); + + kfree(dec); + return 0; +} + +static const struct i2c_device_id wis_saa7113_id[] = { + { "wis_saa7113", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wis_saa7113_id); + +static struct i2c_driver wis_saa7113_driver = { + .driver = { + .name = "WIS SAA7113 I2C driver", + }, + .probe = wis_saa7113_probe, + .remove = wis_saa7113_remove, + .command = wis_saa7113_command, + .id_table = wis_saa7113_id, +}; + +static int __init wis_saa7113_init(void) +{ + return i2c_add_driver(&wis_saa7113_driver); +} + +static void __exit wis_saa7113_cleanup(void) +{ + i2c_del_driver(&wis_saa7113_driver); +} + +module_init(wis_saa7113_init); +module_exit(wis_saa7113_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-saa7115.c b/drivers/staging/media/go7007/wis-saa7115.c new file mode 100644 index 000000000000..46cff59e28b7 --- /dev/null +++ b/drivers/staging/media/go7007/wis-saa7115.c @@ -0,0 +1,469 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "wis-i2c.h" + +struct wis_saa7115 { + int norm; + int brightness; + int contrast; + int saturation; + int hue; +}; + +static u8 initial_registers[] = +{ + 0x01, 0x08, + 0x02, 0xc0, + 0x03, 0x20, + 0x04, 0x80, + 0x05, 0x80, + 0x06, 0xeb, + 0x07, 0xe0, + 0x08, 0xf0, /* always toggle FID */ + 0x09, 0x40, + 0x0a, 0x80, + 0x0b, 0x40, + 0x0c, 0x40, + 0x0d, 0x00, + 0x0e, 0x03, + 0x0f, 0x2a, + 0x10, 0x0e, + 0x11, 0x00, + 0x12, 0x8d, + 0x13, 0x00, + 0x14, 0x00, + 0x15, 0x11, + 0x16, 0x01, + 0x17, 0xda, + 0x18, 0x40, + 0x19, 0x80, + 0x1a, 0x00, + 0x1b, 0x42, + 0x1c, 0xa9, + 0x30, 0x66, + 0x31, 0x90, + 0x32, 0x01, + 0x34, 0x00, + 0x35, 0x00, + 0x36, 0x20, + 0x38, 0x03, + 0x39, 0x20, + 0x3a, 0x88, + 0x40, 0x00, + 0x41, 0xff, + 0x42, 0xff, + 0x43, 0xff, + 0x44, 0xff, + 0x45, 0xff, + 0x46, 0xff, + 0x47, 0xff, + 0x48, 0xff, + 0x49, 0xff, + 0x4a, 0xff, + 0x4b, 0xff, + 0x4c, 0xff, + 0x4d, 0xff, + 0x4e, 0xff, + 0x4f, 0xff, + 0x50, 0xff, + 0x51, 0xff, + 0x52, 0xff, + 0x53, 0xff, + 0x54, 0xf4 /*0xff*/, + 0x55, 0xff, + 0x56, 0xff, + 0x57, 0xff, + 0x58, 0x40, + 0x59, 0x47, + 0x5a, 0x06 /*0x03*/, + 0x5b, 0x83, + 0x5d, 0x06, + 0x5e, 0x00, + 0x80, 0x30, /* window defined scaler operation, task A and B enabled */ + 0x81, 0x03, /* use scaler datapath generated V */ + 0x83, 0x00, + 0x84, 0x00, + 0x85, 0x00, + 0x86, 0x45, + 0x87, 0x31, + 0x88, 0xc0, + 0x90, 0x02, /* task A process top field */ + 0x91, 0x08, + 0x92, 0x09, + 0x93, 0x80, + 0x94, 0x06, + 0x95, 0x00, + 0x96, 0xc0, + 0x97, 0x02, + 0x98, 0x12, + 0x99, 0x00, + 0x9a, 0xf2, + 0x9b, 0x00, + 0x9c, 0xd0, + 0x9d, 0x02, + 0x9e, 0xf2, + 0x9f, 0x00, + 0xa0, 0x01, + 0xa1, 0x01, + 0xa2, 0x01, + 0xa4, 0x80, + 0xa5, 0x40, + 0xa6, 0x40, + 0xa8, 0x00, + 0xa9, 0x04, + 0xaa, 0x00, + 0xac, 0x00, + 0xad, 0x02, + 0xae, 0x00, + 0xb0, 0x00, + 0xb1, 0x04, + 0xb2, 0x00, + 0xb3, 0x04, + 0xb4, 0x00, + 0xb8, 0x00, + 0xbc, 0x00, + 0xc0, 0x03, /* task B process bottom field */ + 0xc1, 0x08, + 0xc2, 0x09, + 0xc3, 0x80, + 0xc4, 0x06, + 0xc5, 0x00, + 0xc6, 0xc0, + 0xc7, 0x02, + 0xc8, 0x12, + 0xc9, 0x00, + 0xca, 0xf2, + 0xcb, 0x00, + 0xcc, 0xd0, + 0xcd, 0x02, + 0xce, 0xf2, + 0xcf, 0x00, + 0xd0, 0x01, + 0xd1, 0x01, + 0xd2, 0x01, + 0xd4, 0x80, + 0xd5, 0x40, + 0xd6, 0x40, + 0xd8, 0x00, + 0xd9, 0x04, + 0xda, 0x00, + 0xdc, 0x00, + 0xdd, 0x02, + 0xde, 0x00, + 0xe0, 0x00, + 0xe1, 0x04, + 0xe2, 0x00, + 0xe3, 0x04, + 0xe4, 0x00, + 0xe8, 0x00, + 0x88, 0xf0, /* End of original static list */ + 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ +}; + +static int write_reg(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int write_regs(struct i2c_client *client, u8 *regs) +{ + int i; + + for (i = 0; regs[i] != 0x00; i += 2) + if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) + return -1; + return 0; +} + +static int wis_saa7115_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct wis_saa7115 *dec = i2c_get_clientdata(client); + + switch (cmd) { + case VIDIOC_S_INPUT: + { + int *input = arg; + + i2c_smbus_write_byte_data(client, 0x02, 0xC0 | *input); + i2c_smbus_write_byte_data(client, 0x09, + *input < 6 ? 0x40 : 0xC0); + break; + } + case DECODER_SET_RESOLUTION: + { + struct video_decoder_resolution *res = arg; + /* Course-grained scaler */ + int h_integer_scaler = res->width < 704 ? 704 / res->width : 1; + /* Fine-grained scaler to take care of remainder */ + int h_scaling_increment = (704 / h_integer_scaler) * + 1024 / res->width; + /* Fine-grained scaler only */ + int v_scaling_increment = (dec->norm & V4L2_STD_NTSC ? + 240 : 288) * 1024 / res->height; + u8 regs[] = { + 0x88, 0xc0, + 0x9c, res->width & 0xff, + 0x9d, res->width >> 8, + 0x9e, res->height & 0xff, + 0x9f, res->height >> 8, + 0xa0, h_integer_scaler, + 0xa1, 1, + 0xa2, 1, + 0xa8, h_scaling_increment & 0xff, + 0xa9, h_scaling_increment >> 8, + 0xac, (h_scaling_increment / 2) & 0xff, + 0xad, (h_scaling_increment / 2) >> 8, + 0xb0, v_scaling_increment & 0xff, + 0xb1, v_scaling_increment >> 8, + 0xb2, v_scaling_increment & 0xff, + 0xb3, v_scaling_increment >> 8, + 0xcc, res->width & 0xff, + 0xcd, res->width >> 8, + 0xce, res->height & 0xff, + 0xcf, res->height >> 8, + 0xd0, h_integer_scaler, + 0xd1, 1, + 0xd2, 1, + 0xd8, h_scaling_increment & 0xff, + 0xd9, h_scaling_increment >> 8, + 0xdc, (h_scaling_increment / 2) & 0xff, + 0xdd, (h_scaling_increment / 2) >> 8, + 0xe0, v_scaling_increment & 0xff, + 0xe1, v_scaling_increment >> 8, + 0xe2, v_scaling_increment & 0xff, + 0xe3, v_scaling_increment >> 8, + 0x88, 0xf0, + 0, 0, + }; + write_regs(client, regs); + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *input = arg; + u8 regs[] = { + 0x88, 0xc0, + 0x98, *input & V4L2_STD_NTSC ? 0x12 : 0x16, + 0x9a, *input & V4L2_STD_NTSC ? 0xf2 : 0x20, + 0x9b, *input & V4L2_STD_NTSC ? 0x00 : 0x01, + 0xc8, *input & V4L2_STD_NTSC ? 0x12 : 0x16, + 0xca, *input & V4L2_STD_NTSC ? 0xf2 : 0x20, + 0xcb, *input & V4L2_STD_NTSC ? 0x00 : 0x01, + 0x88, 0xf0, + 0x30, *input & V4L2_STD_NTSC ? 0x66 : 0x00, + 0x31, *input & V4L2_STD_NTSC ? 0x90 : 0xe0, + 0, 0, + }; + write_regs(client, regs); + dec->norm = *input; + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_CONTRAST: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 64; + ctrl->flags = 0; + break; + case V4L2_CID_SATURATION: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 64; + ctrl->flags = 0; + break; + case V4L2_CID_HUE: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); + ctrl->minimum = -128; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 0; + ctrl->flags = 0; + break; + } + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 255) + dec->brightness = 255; + else if (ctrl->value < 0) + dec->brightness = 0; + else + dec->brightness = ctrl->value; + write_reg(client, 0x0a, dec->brightness); + break; + case V4L2_CID_CONTRAST: + if (ctrl->value > 127) + dec->contrast = 127; + else if (ctrl->value < 0) + dec->contrast = 0; + else + dec->contrast = ctrl->value; + write_reg(client, 0x0b, dec->contrast); + break; + case V4L2_CID_SATURATION: + if (ctrl->value > 127) + dec->saturation = 127; + else if (ctrl->value < 0) + dec->saturation = 0; + else + dec->saturation = ctrl->value; + write_reg(client, 0x0c, dec->saturation); + break; + case V4L2_CID_HUE: + if (ctrl->value > 127) + dec->hue = 127; + else if (ctrl->value < -128) + dec->hue = -128; + else + dec->hue = ctrl->value; + write_reg(client, 0x0d, dec->hue); + break; + } + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dec->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dec->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dec->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dec->hue; + break; + } + break; + } + default: + break; + } + return 0; +} + +static int wis_saa7115_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct wis_saa7115 *dec; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + dec = kmalloc(sizeof(struct wis_saa7115), GFP_KERNEL); + if (dec == NULL) + return -ENOMEM; + + dec->norm = V4L2_STD_NTSC; + dec->brightness = 128; + dec->contrast = 64; + dec->saturation = 64; + dec->hue = 0; + i2c_set_clientdata(client, dec); + + printk(KERN_DEBUG + "wis-saa7115: initializing SAA7115 at address %d on %s\n", + client->addr, adapter->name); + + if (write_regs(client, initial_registers) < 0) { + printk(KERN_ERR + "wis-saa7115: error initializing SAA7115\n"); + kfree(dec); + return -ENODEV; + } + + return 0; +} + +static int wis_saa7115_remove(struct i2c_client *client) +{ + struct wis_saa7115 *dec = i2c_get_clientdata(client); + + kfree(dec); + return 0; +} + +static const struct i2c_device_id wis_saa7115_id[] = { + { "wis_saa7115", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wis_saa7115_id); + +static struct i2c_driver wis_saa7115_driver = { + .driver = { + .name = "WIS SAA7115 I2C driver", + }, + .probe = wis_saa7115_probe, + .remove = wis_saa7115_remove, + .command = wis_saa7115_command, + .id_table = wis_saa7115_id, +}; + +static int __init wis_saa7115_init(void) +{ + return i2c_add_driver(&wis_saa7115_driver); +} + +static void __exit wis_saa7115_cleanup(void) +{ + i2c_del_driver(&wis_saa7115_driver); +} + +module_init(wis_saa7115_init); +module_exit(wis_saa7115_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-sony-tuner.c b/drivers/staging/media/go7007/wis-sony-tuner.c new file mode 100644 index 000000000000..8f1b7d4f6a2e --- /dev/null +++ b/drivers/staging/media/go7007/wis-sony-tuner.c @@ -0,0 +1,720 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wis-i2c.h" + +/* #define MPX_DEBUG */ + +/* AS(IF/MPX) pin: LOW HIGH/OPEN + * IF/MPX address: 0x42/0x40 0x43/0x44 + */ +#define IF_I2C_ADDR 0x43 +#define MPX_I2C_ADDR 0x44 + +static v4l2_std_id force_band; +static char force_band_str[] = "-"; +module_param_string(force_band, force_band_str, sizeof(force_band_str), 0644); +static int force_mpx_mode = -1; +module_param(force_mpx_mode, int, 0644); + +/* Store tuner info in the same format as tuner.c, so maybe we can put the + * Sony tuner support in there. */ +struct sony_tunertype { + char *name; + unsigned char Vendor; /* unused here */ + unsigned char Type; /* unused here */ + + unsigned short thresh1; /* band switch VHF_LO <=> VHF_HI */ + unsigned short thresh2; /* band switch VHF_HI <=> UHF */ + unsigned char VHF_L; + unsigned char VHF_H; + unsigned char UHF; + unsigned char config; + unsigned short IFPCoff; +}; + +/* This array is indexed by (tuner_type - 200) */ +static struct sony_tunertype sony_tuners[] = { + { "Sony PAL+SECAM (BTF-PG472Z)", 0, 0, + 16*144.25, 16*427.25, 0x01, 0x02, 0x04, 0xc6, 623}, + { "Sony NTSC_JP (BTF-PK467Z)", 0, 0, + 16*220.25, 16*467.25, 0x01, 0x02, 0x04, 0xc6, 940}, + { "Sony NTSC (BTF-PB463Z)", 0, 0, + 16*130.25, 16*364.25, 0x01, 0x02, 0x04, 0xc6, 732}, +}; + +struct wis_sony_tuner { + int type; + v4l2_std_id std; + unsigned int freq; + int mpxmode; + u32 audmode; +}; + +/* Basically the same as default_set_tv_freq() in tuner.c */ +static int set_freq(struct i2c_client *client, int freq) +{ + struct wis_sony_tuner *t = i2c_get_clientdata(client); + char *band_name; + int n; + int band_select; + struct sony_tunertype *tun; + u8 buffer[4]; + + tun = &sony_tuners[t->type - 200]; + if (freq < tun->thresh1) { + band_name = "VHF_L"; + band_select = tun->VHF_L; + } else if (freq < tun->thresh2) { + band_name = "VHF_H"; + band_select = tun->VHF_H; + } else { + band_name = "UHF"; + band_select = tun->UHF; + } + printk(KERN_DEBUG "wis-sony-tuner: tuning to frequency %d.%04d (%s)\n", + freq / 16, (freq % 16) * 625, band_name); + n = freq + tun->IFPCoff; + + buffer[0] = n >> 8; + buffer[1] = n & 0xff; + buffer[2] = tun->config; + buffer[3] = band_select; + i2c_master_send(client, buffer, 4); + + return 0; +} + +static int mpx_write(struct i2c_client *client, int dev, int addr, int val) +{ + u8 buffer[5]; + struct i2c_msg msg; + + buffer[0] = dev; + buffer[1] = addr >> 8; + buffer[2] = addr & 0xff; + buffer[3] = val >> 8; + buffer[4] = val & 0xff; + msg.addr = MPX_I2C_ADDR; + msg.flags = 0; + msg.len = 5; + msg.buf = buffer; + i2c_transfer(client->adapter, &msg, 1); + return 0; +} + +/* + * MPX register values for the BTF-PG472Z: + * + * FM_ NICAM_ SCART_ + * MODUS SOURCE ACB PRESCAL PRESCAL PRESCAL SYSTEM VOLUME + * 10/0030 12/0008 12/0013 12/000E 12/0010 12/0000 10/0020 12/0000 + * --------------------------------------------------------------- + * Auto 1003 0020 0100 2603 5000 XXXX 0001 7500 + * + * B/G + * Mono 1003 0020 0100 2603 5000 XXXX 0003 7500 + * A2 1003 0020 0100 2601 5000 XXXX 0003 7500 + * NICAM 1003 0120 0100 2603 5000 XXXX 0008 7500 + * + * I + * Mono 1003 0020 0100 2603 7900 XXXX 000A 7500 + * NICAM 1003 0120 0100 2603 7900 XXXX 000A 7500 + * + * D/K + * Mono 1003 0020 0100 2603 5000 XXXX 0004 7500 + * A2-1 1003 0020 0100 2601 5000 XXXX 0004 7500 + * A2-2 1003 0020 0100 2601 5000 XXXX 0005 7500 + * A2-3 1003 0020 0100 2601 5000 XXXX 0007 7500 + * NICAM 1003 0120 0100 2603 5000 XXXX 000B 7500 + * + * L/L' + * Mono 0003 0200 0100 7C03 5000 2200 0009 7500 + * NICAM 0003 0120 0100 7C03 5000 XXXX 0009 7500 + * + * M + * Mono 1003 0200 0100 2B03 5000 2B00 0002 7500 + * + * For Asia, replace the 0x26XX in FM_PRESCALE with 0x14XX. + * + * Bilingual selection in A2/NICAM: + * + * High byte of SOURCE Left chan Right chan + * 0x01 MAIN SUB + * 0x03 MAIN MAIN + * 0x04 SUB SUB + * + * Force mono in NICAM by setting the high byte of SOURCE to 0x02 (L/L') or + * 0x00 (all other bands). Force mono in A2 with FMONO_A2: + * + * FMONO_A2 + * 10/0022 + * -------- + * Forced mono ON 07F0 + * Forced mono OFF 0190 + */ + +static struct { + enum { AUD_MONO, AUD_A2, AUD_NICAM, AUD_NICAM_L } audio_mode; + u16 modus; + u16 source; + u16 acb; + u16 fm_prescale; + u16 nicam_prescale; + u16 scart_prescale; + u16 system; + u16 volume; +} mpx_audio_modes[] = { + /* Auto */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0001, 0x7500 }, + /* B/G Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0003, 0x7500 }, + /* B/G A2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0003, 0x7500 }, + /* B/G NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0008, 0x7500 }, + /* I Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x7900, 0x0000, 0x000A, 0x7500 }, + /* I NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, + 0x7900, 0x0000, 0x000A, 0x7500 }, + /* D/K Mono */ { AUD_MONO, 0x1003, 0x0020, 0x0100, 0x2603, + 0x5000, 0x0000, 0x0004, 0x7500 }, + /* D/K A2-1 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0004, 0x7500 }, + /* D/K A2-2 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0005, 0x7500 }, + /* D/K A2-3 */ { AUD_A2, 0x1003, 0x0020, 0x0100, 0x2601, + 0x5000, 0x0000, 0x0007, 0x7500 }, + /* D/K NICAM */ { AUD_NICAM, 0x1003, 0x0120, 0x0100, 0x2603, + 0x5000, 0x0000, 0x000B, 0x7500 }, + /* L/L' Mono */ { AUD_MONO, 0x0003, 0x0200, 0x0100, 0x7C03, + 0x5000, 0x2200, 0x0009, 0x7500 }, + /* L/L' NICAM */{ AUD_NICAM_L, 0x0003, 0x0120, 0x0100, 0x7C03, + 0x5000, 0x0000, 0x0009, 0x7500 }, +}; + +#define MPX_NUM_MODES ARRAY_SIZE(mpx_audio_modes) + +static int mpx_setup(struct i2c_client *client) +{ + struct wis_sony_tuner *t = i2c_get_clientdata(client); + u16 source = 0; + u8 buffer[3]; + struct i2c_msg msg; + + /* reset MPX */ + buffer[0] = 0x00; + buffer[1] = 0x80; + buffer[2] = 0x00; + msg.addr = MPX_I2C_ADDR; + msg.flags = 0; + msg.len = 3; + msg.buf = buffer; + i2c_transfer(client->adapter, &msg, 1); + buffer[1] = 0x00; + i2c_transfer(client->adapter, &msg, 1); + + if (mpx_audio_modes[t->mpxmode].audio_mode != AUD_MONO) { + switch (t->audmode) { + case V4L2_TUNER_MODE_MONO: + switch (mpx_audio_modes[t->mpxmode].audio_mode) { + case AUD_A2: + source = mpx_audio_modes[t->mpxmode].source; + break; + case AUD_NICAM: + source = 0x0000; + break; + case AUD_NICAM_L: + source = 0x0200; + break; + default: + break; + } + break; + case V4L2_TUNER_MODE_STEREO: + source = mpx_audio_modes[t->mpxmode].source; + break; + case V4L2_TUNER_MODE_LANG1: + source = 0x0300; + break; + case V4L2_TUNER_MODE_LANG2: + source = 0x0400; + break; + } + source |= mpx_audio_modes[t->mpxmode].source & 0x00ff; + } else + source = mpx_audio_modes[t->mpxmode].source; + + mpx_write(client, 0x10, 0x0030, mpx_audio_modes[t->mpxmode].modus); + mpx_write(client, 0x12, 0x0008, source); + mpx_write(client, 0x12, 0x0013, mpx_audio_modes[t->mpxmode].acb); + mpx_write(client, 0x12, 0x000e, + mpx_audio_modes[t->mpxmode].fm_prescale); + mpx_write(client, 0x12, 0x0010, + mpx_audio_modes[t->mpxmode].nicam_prescale); + mpx_write(client, 0x12, 0x000d, + mpx_audio_modes[t->mpxmode].scart_prescale); + mpx_write(client, 0x10, 0x0020, mpx_audio_modes[t->mpxmode].system); + mpx_write(client, 0x12, 0x0000, mpx_audio_modes[t->mpxmode].volume); + if (mpx_audio_modes[t->mpxmode].audio_mode == AUD_A2) + mpx_write(client, 0x10, 0x0022, + t->audmode == V4L2_TUNER_MODE_MONO ? 0x07f0 : 0x0190); + +#ifdef MPX_DEBUG + { + u8 buf1[3], buf2[2]; + struct i2c_msg msgs[2]; + + printk(KERN_DEBUG "wis-sony-tuner: MPX registers: %04x %04x " + "%04x %04x %04x %04x %04x %04x\n", + mpx_audio_modes[t->mpxmode].modus, + source, + mpx_audio_modes[t->mpxmode].acb, + mpx_audio_modes[t->mpxmode].fm_prescale, + mpx_audio_modes[t->mpxmode].nicam_prescale, + mpx_audio_modes[t->mpxmode].scart_prescale, + mpx_audio_modes[t->mpxmode].system, + mpx_audio_modes[t->mpxmode].volume); + buf1[0] = 0x11; + buf1[1] = 0x00; + buf1[2] = 0x7e; + msgs[0].addr = MPX_I2C_ADDR; + msgs[0].flags = 0; + msgs[0].len = 3; + msgs[0].buf = buf1; + msgs[1].addr = MPX_I2C_ADDR; + msgs[1].flags = I2C_M_RD; + msgs[1].len = 2; + msgs[1].buf = buf2; + i2c_transfer(client->adapter, msgs, 2); + printk(KERN_DEBUG "wis-sony-tuner: MPX system: %02x%02x\n", + buf2[0], buf2[1]); + buf1[0] = 0x11; + buf1[1] = 0x02; + buf1[2] = 0x00; + i2c_transfer(client->adapter, msgs, 2); + printk(KERN_DEBUG "wis-sony-tuner: MPX status: %02x%02x\n", + buf2[0], buf2[1]); + } +#endif + return 0; +} + +/* + * IF configuration values for the BTF-PG472Z: + * + * B/G: 0x94 0x70 0x49 + * I: 0x14 0x70 0x4a + * D/K: 0x14 0x70 0x4b + * L: 0x04 0x70 0x4b + * L': 0x44 0x70 0x53 + * M: 0x50 0x30 0x4c + */ + +static int set_if(struct i2c_client *client) +{ + struct wis_sony_tuner *t = i2c_get_clientdata(client); + u8 buffer[4]; + struct i2c_msg msg; + int default_mpx_mode = 0; + + /* configure IF */ + buffer[0] = 0; + if (t->std & V4L2_STD_PAL_BG) { + buffer[1] = 0x94; + buffer[2] = 0x70; + buffer[3] = 0x49; + default_mpx_mode = 1; + } else if (t->std & V4L2_STD_PAL_I) { + buffer[1] = 0x14; + buffer[2] = 0x70; + buffer[3] = 0x4a; + default_mpx_mode = 4; + } else if (t->std & V4L2_STD_PAL_DK) { + buffer[1] = 0x14; + buffer[2] = 0x70; + buffer[3] = 0x4b; + default_mpx_mode = 6; + } else if (t->std & V4L2_STD_SECAM_L) { + buffer[1] = 0x04; + buffer[2] = 0x70; + buffer[3] = 0x4b; + default_mpx_mode = 11; + } + msg.addr = IF_I2C_ADDR; + msg.flags = 0; + msg.len = 4; + msg.buf = buffer; + i2c_transfer(client->adapter, &msg, 1); + + /* Select MPX mode if not forced by the user */ + if (force_mpx_mode >= 0 && force_mpx_mode < MPX_NUM_MODES) + t->mpxmode = force_mpx_mode; + else + t->mpxmode = default_mpx_mode; + printk(KERN_DEBUG "wis-sony-tuner: setting MPX to mode %d\n", + t->mpxmode); + mpx_setup(client); + + return 0; +} + +static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg) +{ + struct wis_sony_tuner *t = i2c_get_clientdata(client); + + switch (cmd) { +#if 0 +#ifdef TUNER_SET_TYPE_ADDR + case TUNER_SET_TYPE_ADDR: + { + struct tuner_setup *tun_setup = arg; + int *type = &tun_setup->type; +#else + case TUNER_SET_TYPE: + { + int *type = arg; +#endif + + if (t->type >= 0) { + if (t->type != *type) + printk(KERN_ERR "wis-sony-tuner: type already " + "set to %d, ignoring request for %d\n", + t->type, *type); + break; + } + t->type = *type; + switch (t->type) { + case TUNER_SONY_BTF_PG472Z: + switch (force_band_str[0]) { + case 'b': + case 'B': + case 'g': + case 'G': + printk(KERN_INFO "wis-sony-tuner: forcing " + "tuner to PAL-B/G bands\n"); + force_band = V4L2_STD_PAL_BG; + break; + case 'i': + case 'I': + printk(KERN_INFO "wis-sony-tuner: forcing " + "tuner to PAL-I band\n"); + force_band = V4L2_STD_PAL_I; + break; + case 'd': + case 'D': + case 'k': + case 'K': + printk(KERN_INFO "wis-sony-tuner: forcing " + "tuner to PAL-D/K bands\n"); + force_band = V4L2_STD_PAL_I; + break; + case 'l': + case 'L': + printk(KERN_INFO "wis-sony-tuner: forcing " + "tuner to SECAM-L band\n"); + force_band = V4L2_STD_SECAM_L; + break; + default: + force_band = 0; + break; + } + if (force_band) + t->std = force_band; + else + t->std = V4L2_STD_PAL_BG; + set_if(client); + break; + case TUNER_SONY_BTF_PK467Z: + t->std = V4L2_STD_NTSC_M_JP; + break; + case TUNER_SONY_BTF_PB463Z: + t->std = V4L2_STD_NTSC_M; + break; + default: + printk(KERN_ERR "wis-sony-tuner: tuner type %d is not " + "supported by this module\n", *type); + break; + } + if (type >= 0) + printk(KERN_INFO + "wis-sony-tuner: type set to %d (%s)\n", + t->type, sony_tuners[t->type - 200].name); + break; + } +#endif + case VIDIOC_G_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + f->frequency = t->freq; + break; + } + case VIDIOC_S_FREQUENCY: + { + struct v4l2_frequency *f = arg; + + t->freq = f->frequency; + set_freq(client, t->freq); + break; + } + case VIDIOC_ENUMSTD: + { + struct v4l2_standard *std = arg; + + switch (t->type) { + case TUNER_SONY_BTF_PG472Z: + switch (std->index) { + case 0: + v4l2_video_std_construct(std, + V4L2_STD_PAL_BG, "PAL-B/G"); + break; + case 1: + v4l2_video_std_construct(std, + V4L2_STD_PAL_I, "PAL-I"); + break; + case 2: + v4l2_video_std_construct(std, + V4L2_STD_PAL_DK, "PAL-D/K"); + break; + case 3: + v4l2_video_std_construct(std, + V4L2_STD_SECAM_L, "SECAM-L"); + break; + default: + std->id = 0; /* hack to indicate EINVAL */ + break; + } + break; + case TUNER_SONY_BTF_PK467Z: + if (std->index != 0) { + std->id = 0; /* hack to indicate EINVAL */ + break; + } + v4l2_video_std_construct(std, + V4L2_STD_NTSC_M_JP, "NTSC-J"); + break; + case TUNER_SONY_BTF_PB463Z: + if (std->index != 0) { + std->id = 0; /* hack to indicate EINVAL */ + break; + } + v4l2_video_std_construct(std, V4L2_STD_NTSC_M, "NTSC"); + break; + } + break; + } + case VIDIOC_G_STD: + { + v4l2_std_id *std = arg; + + *std = t->std; + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + v4l2_std_id old = t->std; + + switch (t->type) { + case TUNER_SONY_BTF_PG472Z: + if (force_band && (*std & force_band) != *std && + *std != V4L2_STD_PAL && + *std != V4L2_STD_SECAM) { + printk(KERN_DEBUG "wis-sony-tuner: ignoring " + "requested TV standard in " + "favor of force_band value\n"); + t->std = force_band; + } else if (*std & V4L2_STD_PAL_BG) { /* default */ + t->std = V4L2_STD_PAL_BG; + } else if (*std & V4L2_STD_PAL_I) { + t->std = V4L2_STD_PAL_I; + } else if (*std & V4L2_STD_PAL_DK) { + t->std = V4L2_STD_PAL_DK; + } else if (*std & V4L2_STD_SECAM_L) { + t->std = V4L2_STD_SECAM_L; + } else { + printk(KERN_ERR "wis-sony-tuner: TV standard " + "not supported\n"); + *std = 0; /* hack to indicate EINVAL */ + break; + } + if (old != t->std) + set_if(client); + break; + case TUNER_SONY_BTF_PK467Z: + if (!(*std & V4L2_STD_NTSC_M_JP)) { + printk(KERN_ERR "wis-sony-tuner: TV standard " + "not supported\n"); + *std = 0; /* hack to indicate EINVAL */ + } + break; + case TUNER_SONY_BTF_PB463Z: + if (!(*std & V4L2_STD_NTSC_M)) { + printk(KERN_ERR "wis-sony-tuner: TV standard " + "not supported\n"); + *std = 0; /* hack to indicate EINVAL */ + } + break; + } + break; + } + case VIDIOC_QUERYSTD: + { + v4l2_std_id *std = arg; + + switch (t->type) { + case TUNER_SONY_BTF_PG472Z: + if (force_band) + *std = force_band; + else + *std = V4L2_STD_PAL_BG | V4L2_STD_PAL_I | + V4L2_STD_PAL_DK | V4L2_STD_SECAM_L; + break; + case TUNER_SONY_BTF_PK467Z: + *std = V4L2_STD_NTSC_M_JP; + break; + case TUNER_SONY_BTF_PB463Z: + *std = V4L2_STD_NTSC_M; + break; + } + break; + } + case VIDIOC_G_TUNER: + { + struct v4l2_tuner *tun = arg; + + memset(tun, 0, sizeof(*tun)); + strcpy(tun->name, "Television"); + tun->type = V4L2_TUNER_ANALOG_TV; + tun->rangelow = 0UL; /* does anything use these? */ + tun->rangehigh = 0xffffffffUL; + switch (t->type) { + case TUNER_SONY_BTF_PG472Z: + tun->capability = V4L2_TUNER_CAP_NORM | + V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 | + V4L2_TUNER_CAP_LANG2; + tun->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_LANG1 | + V4L2_TUNER_SUB_LANG2; + break; + case TUNER_SONY_BTF_PK467Z: + case TUNER_SONY_BTF_PB463Z: + tun->capability = V4L2_TUNER_CAP_STEREO; + tun->rxsubchans = V4L2_TUNER_SUB_MONO | + V4L2_TUNER_SUB_STEREO; + break; + } + tun->audmode = t->audmode; + return 0; + } + case VIDIOC_S_TUNER: + { + struct v4l2_tuner *tun = arg; + + switch (t->type) { + case TUNER_SONY_BTF_PG472Z: + if (tun->audmode != t->audmode) { + t->audmode = tun->audmode; + mpx_setup(client); + } + break; + case TUNER_SONY_BTF_PK467Z: + case TUNER_SONY_BTF_PB463Z: + break; + } + return 0; + } + default: + break; + } + return 0; +} + +static int wis_sony_tuner_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct wis_sony_tuner *t; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENODEV; + + t = kmalloc(sizeof(struct wis_sony_tuner), GFP_KERNEL); + if (t == NULL) + return -ENOMEM; + + t->type = -1; + t->freq = 0; + t->mpxmode = 0; + t->audmode = V4L2_TUNER_MODE_STEREO; + i2c_set_clientdata(client, t); + + printk(KERN_DEBUG + "wis-sony-tuner: initializing tuner at address %d on %s\n", + client->addr, adapter->name); + + return 0; +} + +static int wis_sony_tuner_remove(struct i2c_client *client) +{ + struct wis_sony_tuner *t = i2c_get_clientdata(client); + + kfree(t); + return 0; +} + +static const struct i2c_device_id wis_sony_tuner_id[] = { + { "wis_sony_tuner", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wis_sony_tuner_id); + +static struct i2c_driver wis_sony_tuner_driver = { + .driver = { + .name = "WIS Sony TV Tuner I2C driver", + }, + .probe = wis_sony_tuner_probe, + .remove = wis_sony_tuner_remove, + .command = tuner_command, + .id_table = wis_sony_tuner_id, +}; + +static int __init wis_sony_tuner_init(void) +{ + return i2c_add_driver(&wis_sony_tuner_driver); +} + +static void __exit wis_sony_tuner_cleanup(void) +{ + i2c_del_driver(&wis_sony_tuner_driver); +} + +module_init(wis_sony_tuner_init); +module_exit(wis_sony_tuner_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-tw2804.c b/drivers/staging/media/go7007/wis-tw2804.c new file mode 100644 index 000000000000..9134f03e3cf0 --- /dev/null +++ b/drivers/staging/media/go7007/wis-tw2804.c @@ -0,0 +1,357 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "wis-i2c.h" + +struct wis_tw2804 { + int channel; + int norm; + int brightness; + int contrast; + int saturation; + int hue; +}; + +static u8 global_registers[] = { + 0x39, 0x00, + 0x3a, 0xff, + 0x3b, 0x84, + 0x3c, 0x80, + 0x3d, 0x80, + 0x3e, 0x82, + 0x3f, 0x82, + 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ +}; + +static u8 channel_registers[] = { + 0x01, 0xc4, + 0x02, 0xa5, + 0x03, 0x20, + 0x04, 0xd0, + 0x05, 0x20, + 0x06, 0xd0, + 0x07, 0x88, + 0x08, 0x20, + 0x09, 0x07, + 0x0a, 0xf0, + 0x0b, 0x07, + 0x0c, 0xf0, + 0x0d, 0x40, + 0x0e, 0xd2, + 0x0f, 0x80, + 0x10, 0x80, + 0x11, 0x80, + 0x12, 0x80, + 0x13, 0x1f, + 0x14, 0x00, + 0x15, 0x00, + 0x16, 0x00, + 0x17, 0x00, + 0x18, 0xff, + 0x19, 0xff, + 0x1a, 0xff, + 0x1b, 0xff, + 0x1c, 0xff, + 0x1d, 0xff, + 0x1e, 0xff, + 0x1f, 0xff, + 0x20, 0x07, + 0x21, 0x07, + 0x22, 0x00, + 0x23, 0x91, + 0x24, 0x51, + 0x25, 0x03, + 0x26, 0x00, + 0x27, 0x00, + 0x28, 0x00, + 0x29, 0x00, + 0x2a, 0x00, + 0x2b, 0x00, + 0x2c, 0x00, + 0x2d, 0x00, + 0x2e, 0x00, + 0x2f, 0x00, + 0x30, 0x00, + 0x31, 0x00, + 0x32, 0x00, + 0x33, 0x00, + 0x34, 0x00, + 0x35, 0x00, + 0x36, 0x00, + 0x37, 0x00, + 0xff, 0xff, /* Terminator (reg 0xff does not exist) */ +}; + +static int write_reg(struct i2c_client *client, u8 reg, u8 value, int channel) +{ + return i2c_smbus_write_byte_data(client, reg | (channel << 6), value); +} + +static int write_regs(struct i2c_client *client, u8 *regs, int channel) +{ + int i; + + for (i = 0; regs[i] != 0xff; i += 2) + if (i2c_smbus_write_byte_data(client, + regs[i] | (channel << 6), regs[i + 1]) < 0) + return -1; + return 0; +} + +static int wis_tw2804_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct wis_tw2804 *dec = i2c_get_clientdata(client); + + if (cmd == DECODER_SET_CHANNEL) { + int *input = arg; + + if (*input < 0 || *input > 3) { + printk(KERN_ERR "wis-tw2804: channel %d is not " + "between 0 and 3!\n", *input); + return 0; + } + dec->channel = *input; + printk(KERN_DEBUG "wis-tw2804: initializing TW2804 " + "channel %d\n", dec->channel); + if (dec->channel == 0 && + write_regs(client, global_registers, 0) < 0) { + printk(KERN_ERR "wis-tw2804: error initializing " + "TW2804 global registers\n"); + return 0; + } + if (write_regs(client, channel_registers, dec->channel) < 0) { + printk(KERN_ERR "wis-tw2804: error initializing " + "TW2804 channel %d\n", dec->channel); + return 0; + } + return 0; + } + + if (dec->channel < 0) { + printk(KERN_DEBUG "wis-tw2804: ignoring command %08x until " + "channel number is set\n", cmd); + return 0; + } + + switch (cmd) { + case VIDIOC_S_STD: + { + v4l2_std_id *input = arg; + u8 regs[] = { + 0x01, *input & V4L2_STD_NTSC ? 0xc4 : 0x84, + 0x09, *input & V4L2_STD_NTSC ? 0x07 : 0x04, + 0x0a, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, + 0x0b, *input & V4L2_STD_NTSC ? 0x07 : 0x04, + 0x0c, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, + 0x0d, *input & V4L2_STD_NTSC ? 0x40 : 0x4a, + 0x16, *input & V4L2_STD_NTSC ? 0x00 : 0x40, + 0x17, *input & V4L2_STD_NTSC ? 0x00 : 0x40, + 0x20, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, + 0x21, *input & V4L2_STD_NTSC ? 0x07 : 0x0f, + 0xff, 0xff, + }; + write_regs(client, regs, dec->channel); + dec->norm = *input; + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_CONTRAST: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_SATURATION: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + case V4L2_CID_HUE: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 128; + ctrl->flags = 0; + break; + } + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 255) + dec->brightness = 255; + else if (ctrl->value < 0) + dec->brightness = 0; + else + dec->brightness = ctrl->value; + write_reg(client, 0x12, dec->brightness, dec->channel); + break; + case V4L2_CID_CONTRAST: + if (ctrl->value > 255) + dec->contrast = 255; + else if (ctrl->value < 0) + dec->contrast = 0; + else + dec->contrast = ctrl->value; + write_reg(client, 0x11, dec->contrast, dec->channel); + break; + case V4L2_CID_SATURATION: + if (ctrl->value > 255) + dec->saturation = 255; + else if (ctrl->value < 0) + dec->saturation = 0; + else + dec->saturation = ctrl->value; + write_reg(client, 0x10, dec->saturation, dec->channel); + break; + case V4L2_CID_HUE: + if (ctrl->value > 255) + dec->hue = 255; + else if (ctrl->value < 0) + dec->hue = 0; + else + dec->hue = ctrl->value; + write_reg(client, 0x0f, dec->hue, dec->channel); + break; + } + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dec->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dec->contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = dec->saturation; + break; + case V4L2_CID_HUE: + ctrl->value = dec->hue; + break; + } + break; + } + default: + break; + } + return 0; +} + +static int wis_tw2804_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct wis_tw2804 *dec; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + dec = kmalloc(sizeof(struct wis_tw2804), GFP_KERNEL); + if (dec == NULL) + return -ENOMEM; + + dec->channel = -1; + dec->norm = V4L2_STD_NTSC; + dec->brightness = 128; + dec->contrast = 128; + dec->saturation = 128; + dec->hue = 128; + i2c_set_clientdata(client, dec); + + printk(KERN_DEBUG "wis-tw2804: creating TW2804 at address %d on %s\n", + client->addr, adapter->name); + + return 0; +} + +static int wis_tw2804_remove(struct i2c_client *client) +{ + struct wis_tw2804 *dec = i2c_get_clientdata(client); + + kfree(dec); + return 0; +} + +static const struct i2c_device_id wis_tw2804_id[] = { + { "wis_tw2804", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wis_tw2804_id); + +static struct i2c_driver wis_tw2804_driver = { + .driver = { + .name = "WIS TW2804 I2C driver", + }, + .probe = wis_tw2804_probe, + .remove = wis_tw2804_remove, + .command = wis_tw2804_command, + .id_table = wis_tw2804_id, +}; + +static int __init wis_tw2804_init(void) +{ + return i2c_add_driver(&wis_tw2804_driver); +} + +static void __exit wis_tw2804_cleanup(void) +{ + i2c_del_driver(&wis_tw2804_driver); +} + +module_init(wis_tw2804_init); +module_exit(wis_tw2804_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-tw9903.c b/drivers/staging/media/go7007/wis-tw9903.c new file mode 100644 index 000000000000..9230f4a80529 --- /dev/null +++ b/drivers/staging/media/go7007/wis-tw9903.c @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "wis-i2c.h" + +struct wis_tw9903 { + int norm; + int brightness; + int contrast; + int hue; +}; + +static u8 initial_registers[] = +{ + 0x02, 0x44, /* input 1, composite */ + 0x03, 0x92, /* correct digital format */ + 0x04, 0x00, + 0x05, 0x80, /* or 0x00 for PAL */ + 0x06, 0x40, /* second internal current reference */ + 0x07, 0x02, /* window */ + 0x08, 0x14, /* window */ + 0x09, 0xf0, /* window */ + 0x0a, 0x81, /* window */ + 0x0b, 0xd0, /* window */ + 0x0c, 0x8c, + 0x0d, 0x00, /* scaling */ + 0x0e, 0x11, /* scaling */ + 0x0f, 0x00, /* scaling */ + 0x10, 0x00, /* brightness */ + 0x11, 0x60, /* contrast */ + 0x12, 0x01, /* sharpness */ + 0x13, 0x7f, /* U gain */ + 0x14, 0x5a, /* V gain */ + 0x15, 0x00, /* hue */ + 0x16, 0xc3, /* sharpness */ + 0x18, 0x00, + 0x19, 0x58, /* vbi */ + 0x1a, 0x80, + 0x1c, 0x0f, /* video norm */ + 0x1d, 0x7f, /* video norm */ + 0x20, 0xa0, /* clamping gain (working 0x50) */ + 0x21, 0x22, + 0x22, 0xf0, + 0x23, 0xfe, + 0x24, 0x3c, + 0x25, 0x38, + 0x26, 0x44, + 0x27, 0x20, + 0x28, 0x00, + 0x29, 0x15, + 0x2a, 0xa0, + 0x2b, 0x44, + 0x2c, 0x37, + 0x2d, 0x00, + 0x2e, 0xa5, /* burst PLL control (working: a9) */ + 0x2f, 0xe0, /* 0xea is blue test frame -- 0xe0 for normal */ + 0x31, 0x00, + 0x33, 0x22, + 0x34, 0x11, + 0x35, 0x35, + 0x3b, 0x05, + 0x06, 0xc0, /* reset device */ + 0x00, 0x00, /* Terminator (reg 0x00 is read-only) */ +}; + +static int write_reg(struct i2c_client *client, u8 reg, u8 value) +{ + return i2c_smbus_write_byte_data(client, reg, value); +} + +static int write_regs(struct i2c_client *client, u8 *regs) +{ + int i; + + for (i = 0; regs[i] != 0x00; i += 2) + if (i2c_smbus_write_byte_data(client, regs[i], regs[i + 1]) < 0) + return -1; + return 0; +} + +static int wis_tw9903_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct wis_tw9903 *dec = i2c_get_clientdata(client); + + switch (cmd) { + case VIDIOC_S_INPUT: + { + int *input = arg; + + i2c_smbus_write_byte_data(client, 0x02, 0x40 | (*input << 1)); + break; + } +#if 0 + /* The scaler on this thing seems to be horribly broken */ + case DECODER_SET_RESOLUTION: + { + struct video_decoder_resolution *res = arg; + /*int hscale = 256 * 720 / res->width;*/ + int hscale = 256 * 720 / (res->width - (res->width > 704 ? 0 : 8)); + int vscale = 256 * (dec->norm & V4L2_STD_NTSC ? 240 : 288) + / res->height; + u8 regs[] = { + 0x0d, vscale & 0xff, + 0x0f, hscale & 0xff, + 0x0e, ((vscale & 0xf00) >> 4) | ((hscale & 0xf00) >> 8), + 0x06, 0xc0, /* reset device */ + 0, 0, + }; + printk(KERN_DEBUG "vscale is %04x, hscale is %04x\n", + vscale, hscale); + /*write_regs(client, regs);*/ + break; + } +#endif + case VIDIOC_S_STD: + { + v4l2_std_id *input = arg; + u8 regs[] = { + 0x05, *input & V4L2_STD_NTSC ? 0x80 : 0x00, + 0x07, *input & V4L2_STD_NTSC ? 0x02 : 0x12, + 0x08, *input & V4L2_STD_NTSC ? 0x14 : 0x18, + 0x09, *input & V4L2_STD_NTSC ? 0xf0 : 0x20, + 0, 0, + }; + write_regs(client, regs); + dec->norm = *input; + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)); + ctrl->minimum = -128; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 0x00; + ctrl->flags = 0; + break; + case V4L2_CID_CONTRAST: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 255; + ctrl->step = 1; + ctrl->default_value = 0x60; + ctrl->flags = 0; + break; +#if 0 + /* I don't understand how the Chroma Gain registers work... */ + case V4L2_CID_SATURATION: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)); + ctrl->minimum = 0; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 64; + ctrl->flags = 0; + break; +#endif + case V4L2_CID_HUE: + ctrl->type = V4L2_CTRL_TYPE_INTEGER; + strncpy(ctrl->name, "Hue", sizeof(ctrl->name)); + ctrl->minimum = -128; + ctrl->maximum = 127; + ctrl->step = 1; + ctrl->default_value = 0; + ctrl->flags = 0; + break; + } + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + if (ctrl->value > 127) + dec->brightness = 127; + else if (ctrl->value < -128) + dec->brightness = -128; + else + dec->brightness = ctrl->value; + write_reg(client, 0x10, dec->brightness); + break; + case V4L2_CID_CONTRAST: + if (ctrl->value > 255) + dec->contrast = 255; + else if (ctrl->value < 0) + dec->contrast = 0; + else + dec->contrast = ctrl->value; + write_reg(client, 0x11, dec->contrast); + break; +#if 0 + case V4L2_CID_SATURATION: + if (ctrl->value > 127) + dec->saturation = 127; + else if (ctrl->value < 0) + dec->saturation = 0; + else + dec->saturation = ctrl->value; + /*write_reg(client, 0x0c, dec->saturation);*/ + break; +#endif + case V4L2_CID_HUE: + if (ctrl->value > 127) + dec->hue = 127; + else if (ctrl->value < -128) + dec->hue = -128; + else + dec->hue = ctrl->value; + write_reg(client, 0x15, dec->hue); + break; + } + break; + } + case VIDIOC_G_CTRL: + { + struct v4l2_control *ctrl = arg; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = dec->brightness; + break; + case V4L2_CID_CONTRAST: + ctrl->value = dec->contrast; + break; +#if 0 + case V4L2_CID_SATURATION: + ctrl->value = dec->saturation; + break; +#endif + case V4L2_CID_HUE: + ctrl->value = dec->hue; + break; + } + break; + } + default: + break; + } + return 0; +} + +static int wis_tw9903_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + struct wis_tw9903 *dec; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + dec = kmalloc(sizeof(struct wis_tw9903), GFP_KERNEL); + if (dec == NULL) + return -ENOMEM; + + dec->norm = V4L2_STD_NTSC; + dec->brightness = 0; + dec->contrast = 0x60; + dec->hue = 0; + i2c_set_clientdata(client, dec); + + printk(KERN_DEBUG + "wis-tw9903: initializing TW9903 at address %d on %s\n", + client->addr, adapter->name); + + if (write_regs(client, initial_registers) < 0) { + printk(KERN_ERR "wis-tw9903: error initializing TW9903\n"); + kfree(dec); + return -ENODEV; + } + + return 0; +} + +static int wis_tw9903_remove(struct i2c_client *client) +{ + struct wis_tw9903 *dec = i2c_get_clientdata(client); + + kfree(dec); + return 0; +} + +static const struct i2c_device_id wis_tw9903_id[] = { + { "wis_tw9903", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wis_tw9903_id); + +static struct i2c_driver wis_tw9903_driver = { + .driver = { + .name = "WIS TW9903 I2C driver", + }, + .probe = wis_tw9903_probe, + .remove = wis_tw9903_remove, + .command = wis_tw9903_command, + .id_table = wis_tw9903_id, +}; + +static int __init wis_tw9903_init(void) +{ + return i2c_add_driver(&wis_tw9903_driver); +} + +static void __exit wis_tw9903_cleanup(void) +{ + i2c_del_driver(&wis_tw9903_driver); +} + +module_init(wis_tw9903_init); +module_exit(wis_tw9903_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/go7007/wis-uda1342.c b/drivers/staging/media/go7007/wis-uda1342.c new file mode 100644 index 000000000000..0127be2f3be0 --- /dev/null +++ b/drivers/staging/media/go7007/wis-uda1342.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2005-2006 Micronas USA 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., 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. + */ + +#include +#include +#include +#include +#include +#include + +#include "wis-i2c.h" + +static int write_reg(struct i2c_client *client, int reg, int value) +{ + /* UDA1342 wants MSB first, but SMBus sends LSB first */ + i2c_smbus_write_word_data(client, reg, swab16(value)); + return 0; +} + +static int wis_uda1342_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + switch (cmd) { + case VIDIOC_S_AUDIO: + { + int *inp = arg; + + switch (*inp) { + case TVAUDIO_INPUT_TUNER: + write_reg(client, 0x00, 0x1441); /* select input 2 */ + break; + case TVAUDIO_INPUT_EXTERN: + write_reg(client, 0x00, 0x1241); /* select input 1 */ + break; + default: + printk(KERN_ERR "wis-uda1342: input %d not supported\n", + *inp); + break; + } + break; + } + default: + break; + } + return 0; +} + +static int wis_uda1342_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -ENODEV; + + printk(KERN_DEBUG + "wis-uda1342: initializing UDA1342 at address %d on %s\n", + client->addr, adapter->name); + + write_reg(client, 0x00, 0x8000); /* reset registers */ + write_reg(client, 0x00, 0x1241); /* select input 1 */ + + return 0; +} + +static int wis_uda1342_remove(struct i2c_client *client) +{ + return 0; +} + +static const struct i2c_device_id wis_uda1342_id[] = { + { "wis_uda1342", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, wis_uda1342_id); + +static struct i2c_driver wis_uda1342_driver = { + .driver = { + .name = "WIS UDA1342 I2C driver", + }, + .probe = wis_uda1342_probe, + .remove = wis_uda1342_remove, + .command = wis_uda1342_command, + .id_table = wis_uda1342_id, +}; + +static int __init wis_uda1342_init(void) +{ + return i2c_add_driver(&wis_uda1342_driver); +} + +static void __exit wis_uda1342_cleanup(void) +{ + i2c_del_driver(&wis_uda1342_driver); +} + +module_init(wis_uda1342_init); +module_exit(wis_uda1342_cleanup); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/staging/media/lirc/Kconfig b/drivers/staging/media/lirc/Kconfig new file mode 100644 index 000000000000..526ec0fc2f04 --- /dev/null +++ b/drivers/staging/media/lirc/Kconfig @@ -0,0 +1,78 @@ +# +# LIRC driver(s) configuration +# +menuconfig LIRC_STAGING + bool "Linux Infrared Remote Control IR receiver/transmitter drivers" + depends on LIRC + help + Say Y here, and all supported Linux Infrared Remote Control IR and + RF receiver and transmitter drivers will be displayed. When paired + with a remote control and the lirc daemon, the receiver drivers + allow control of your Linux system via remote control. + +if LIRC_STAGING + +config LIRC_BT829 + tristate "BT829 based hardware" + depends on LIRC && PCI + help + Driver for the IR interface on BT829-based hardware + +config LIRC_IGORPLUGUSB + tristate "Igor Cesko's USB IR Receiver" + depends on LIRC && USB + help + Driver for Igor Cesko's USB IR Receiver + +config LIRC_IMON + tristate "Legacy SoundGraph iMON Receiver and Display" + depends on LIRC && USB + help + Driver for the original SoundGraph iMON IR Receiver and Display + + Current generation iMON devices use the input layer imon driver. + +config LIRC_PARALLEL + tristate "Homebrew Parallel Port Receiver" + depends on LIRC && PARPORT + help + Driver for Homebrew Parallel Port Receivers + +config LIRC_SASEM + tristate "Sasem USB IR Remote" + depends on LIRC && USB + help + Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module + +config LIRC_SERIAL + tristate "Homebrew Serial Port Receiver" + depends on LIRC + help + Driver for Homebrew Serial Port Receivers + +config LIRC_SERIAL_TRANSMITTER + bool "Serial Port Transmitter" + default y + depends on LIRC_SERIAL + help + Serial Port Transmitter support + +config LIRC_SIR + tristate "Built-in SIR IrDA port" + depends on LIRC + help + Driver for the SIR IrDA port + +config LIRC_TTUSBIR + tristate "Technotrend USB IR Receiver" + depends on LIRC && USB + help + Driver for the Technotrend USB IR Receiver + +config LIRC_ZILOG + tristate "Zilog/Hauppauge IR Transmitter" + depends on LIRC && I2C + help + Driver for the Zilog/Hauppauge IR Transmitter, found on + PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards +endif diff --git a/drivers/staging/media/lirc/Makefile b/drivers/staging/media/lirc/Makefile new file mode 100644 index 000000000000..d76b0fa2af53 --- /dev/null +++ b/drivers/staging/media/lirc/Makefile @@ -0,0 +1,14 @@ +# Makefile for the lirc drivers. +# + +# Each configuration option enables a list of files. + +obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o +obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o +obj-$(CONFIG_LIRC_IMON) += lirc_imon.o +obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o +obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o +obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o +obj-$(CONFIG_LIRC_SIR) += lirc_sir.o +obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o +obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o diff --git a/drivers/staging/media/lirc/TODO b/drivers/staging/media/lirc/TODO new file mode 100644 index 000000000000..b6cb593f55c6 --- /dev/null +++ b/drivers/staging/media/lirc/TODO @@ -0,0 +1,8 @@ +- All drivers should either be ported to ir-core, or dropped entirely + (see drivers/media/IR/mceusb.c vs. lirc_mceusb.c in lirc cvs for an + example of a previously completed port). + +Please send patches to: +Jarod Wilson +Greg Kroah-Hartman + diff --git a/drivers/staging/media/lirc/TODO.lirc_zilog b/drivers/staging/media/lirc/TODO.lirc_zilog new file mode 100644 index 000000000000..a97800a8e127 --- /dev/null +++ b/drivers/staging/media/lirc/TODO.lirc_zilog @@ -0,0 +1,36 @@ +1. Both ir-kbd-i2c and lirc_zilog provide support for RX events for +the chips supported by lirc_zilog. Before moving lirc_zilog out of staging: + +a. ir-kbd-i2c needs a module parameter added to allow the user to tell + ir-kbd-i2c to ignore Z8 IR units. + +b. lirc_zilog should provide Rx key presses to the rc core like ir-kbd-i2c + does. + + +2. lirc_zilog module ref-counting need examination. It has not been +verified that cdev and lirc_dev will take the proper module references on +lirc_zilog to prevent removal of lirc_zilog when the /dev/lircN device node +is open. + +(The good news is ref-counting of lirc_zilog internal structures appears to be +complete. Testing has shown the cx18 module can be unloaded out from under +irw + lircd + lirc_dev, with the /dev/lirc0 device node open, with no adverse +effects. The cx18 module could then be reloaded and irw properly began +receiving button presses again and ir_send worked without error.) + + +3. Bridge drivers, if able, should provide a chip reset() callback +to lirc_zilog via struct IR_i2c_init_data. cx18 and ivtv already have routines +to perform Z8 chip resets via GPIO manipulations. This would allow lirc_zilog +to bring the chip back to normal when it hangs, in the same places the +original lirc_pvr150 driver code does. This is not strictly needed, so it +is not required to move lirc_zilog out of staging. + +Note: Both lirc_zilog and ir-kbd-i2c support the Zilog Z8 for IR, as programmed +and installed on Hauppauge products. When working on either module, developers +must consider at least the following bridge drivers which mention an IR Rx unit +at address 0x71 (indicative of a Z8): + + ivtv cx18 hdpvr pvrusb2 bt8xx cx88 saa7134 + diff --git a/drivers/staging/media/lirc/lirc_bt829.c b/drivers/staging/media/lirc/lirc_bt829.c new file mode 100644 index 000000000000..c5a0d27a02dc --- /dev/null +++ b/drivers/staging/media/lirc/lirc_bt829.c @@ -0,0 +1,383 @@ +/* + * Remote control driver for the TV-card based on bt829 + * + * by Leonid Froenchenko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +static int poll_main(void); +static int atir_init_start(void); + +static void write_index(unsigned char index, unsigned int value); +static unsigned int read_index(unsigned char index); + +static void do_i2c_start(void); +static void do_i2c_stop(void); + +static void seems_wr_byte(unsigned char al); +static unsigned char seems_rd_byte(void); + +static unsigned int read_index(unsigned char al); +static void write_index(unsigned char ah, unsigned int edx); + +static void cycle_delay(int cycle); + +static void do_set_bits(unsigned char bl); +static unsigned char do_get_bits(void); + +#define DATA_PCI_OFF 0x7FFC00 +#define WAIT_CYCLE 20 + +#define DRIVER_NAME "lirc_bt829" + +static int debug; +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \ + } while (0) + +static int atir_minor; +static unsigned long pci_addr_phys; +static unsigned char *pci_addr_lin; + +static struct lirc_driver atir_driver; + +static struct pci_dev *do_pci_probe(void) +{ + struct pci_dev *my_dev; + my_dev = pci_get_device(PCI_VENDOR_ID_ATI, + PCI_DEVICE_ID_ATI_264VT, NULL); + if (my_dev) { + printk(KERN_ERR DRIVER_NAME ": Using device: %s\n", + pci_name(my_dev)); + pci_addr_phys = 0; + if (my_dev->resource[0].flags & IORESOURCE_MEM) { + pci_addr_phys = my_dev->resource[0].start; + printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X\n", + (unsigned int)pci_addr_phys); + } + if (pci_addr_phys == 0) { + printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n"); + return NULL; + } + } else { + printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n"); + return NULL; + } + return my_dev; +} + +static int atir_add_to_buf(void *data, struct lirc_buffer *buf) +{ + unsigned char key; + int status; + status = poll_main(); + key = (status >> 8) & 0xFF; + if (status & 0xFF) { + dprintk("reading key %02X\n", key); + lirc_buffer_write(buf, &key); + return 0; + } + return -ENODATA; +} + +static int atir_set_use_inc(void *data) +{ + dprintk("driver is opened\n"); + return 0; +} + +static void atir_set_use_dec(void *data) +{ + dprintk("driver is closed\n"); +} + +int init_module(void) +{ + struct pci_dev *pdev; + + pdev = do_pci_probe(); + if (pdev == NULL) + return -ENODEV; + + if (!atir_init_start()) + return -ENODEV; + + strcpy(atir_driver.name, "ATIR"); + atir_driver.minor = -1; + atir_driver.code_length = 8; + atir_driver.sample_rate = 10; + atir_driver.data = 0; + atir_driver.add_to_buf = atir_add_to_buf; + atir_driver.set_use_inc = atir_set_use_inc; + atir_driver.set_use_dec = atir_set_use_dec; + atir_driver.dev = &pdev->dev; + atir_driver.owner = THIS_MODULE; + + atir_minor = lirc_register_driver(&atir_driver); + if (atir_minor < 0) { + printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n"); + return atir_minor; + } + dprintk("driver is registered on minor %d\n", atir_minor); + + return 0; +} + + +void cleanup_module(void) +{ + lirc_unregister_driver(atir_minor); +} + + +static int atir_init_start(void) +{ + pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400); + if (pci_addr_lin == 0) { + printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n"); + return 0; + } + return 1; +} + +static void cycle_delay(int cycle) +{ + udelay(WAIT_CYCLE*cycle); +} + + +static int poll_main() +{ + unsigned char status_high, status_low; + + do_i2c_start(); + + seems_wr_byte(0xAA); + seems_wr_byte(0x01); + + do_i2c_start(); + + seems_wr_byte(0xAB); + + status_low = seems_rd_byte(); + status_high = seems_rd_byte(); + + do_i2c_stop(); + + return (status_high << 8) | status_low; +} + +static void do_i2c_start(void) +{ + do_set_bits(3); + cycle_delay(4); + + do_set_bits(1); + cycle_delay(7); + + do_set_bits(0); + cycle_delay(2); +} + +static void do_i2c_stop(void) +{ + unsigned char bits; + bits = do_get_bits() & 0xFD; + do_set_bits(bits); + cycle_delay(1); + + bits |= 1; + do_set_bits(bits); + cycle_delay(2); + + bits |= 2; + do_set_bits(bits); + bits = 3; + do_set_bits(bits); + cycle_delay(2); +} + +static void seems_wr_byte(unsigned char value) +{ + int i; + unsigned char reg; + + reg = do_get_bits(); + for (i = 0; i < 8; i++) { + if (value & 0x80) + reg |= 0x02; + else + reg &= 0xFD; + + do_set_bits(reg); + cycle_delay(1); + + reg |= 1; + do_set_bits(reg); + cycle_delay(1); + + reg &= 0xFE; + do_set_bits(reg); + cycle_delay(1); + value <<= 1; + } + cycle_delay(2); + + reg |= 2; + do_set_bits(reg); + + reg |= 1; + do_set_bits(reg); + + cycle_delay(1); + do_get_bits(); + + reg &= 0xFE; + do_set_bits(reg); + cycle_delay(3); +} + +static unsigned char seems_rd_byte(void) +{ + int i; + int rd_byte; + unsigned char bits_2, bits_1; + + bits_1 = do_get_bits() | 2; + do_set_bits(bits_1); + + rd_byte = 0; + for (i = 0; i < 8; i++) { + bits_1 &= 0xFE; + do_set_bits(bits_1); + cycle_delay(2); + + bits_1 |= 1; + do_set_bits(bits_1); + cycle_delay(1); + + bits_2 = do_get_bits(); + if (bits_2 & 2) + rd_byte |= 1; + + rd_byte <<= 1; + } + + bits_1 = 0; + if (bits_2 == 0) + bits_1 |= 2; + + do_set_bits(bits_1); + cycle_delay(2); + + bits_1 |= 1; + do_set_bits(bits_1); + cycle_delay(3); + + bits_1 &= 0xFE; + do_set_bits(bits_1); + cycle_delay(2); + + rd_byte >>= 1; + rd_byte &= 0xFF; + return rd_byte; +} + +static void do_set_bits(unsigned char new_bits) +{ + int reg_val; + reg_val = read_index(0x34); + if (new_bits & 2) { + reg_val &= 0xFFFFFFDF; + reg_val |= 1; + } else { + reg_val &= 0xFFFFFFFE; + reg_val |= 0x20; + } + reg_val |= 0x10; + write_index(0x34, reg_val); + + reg_val = read_index(0x31); + if (new_bits & 1) + reg_val |= 0x1000000; + else + reg_val &= 0xFEFFFFFF; + + reg_val |= 0x8000000; + write_index(0x31, reg_val); +} + +static unsigned char do_get_bits(void) +{ + unsigned char bits; + int reg_val; + + reg_val = read_index(0x34); + reg_val |= 0x10; + reg_val &= 0xFFFFFFDF; + write_index(0x34, reg_val); + + reg_val = read_index(0x34); + bits = 0; + if (reg_val & 8) + bits |= 2; + else + bits &= 0xFD; + + reg_val = read_index(0x31); + if (reg_val & 0x1000000) + bits |= 1; + else + bits &= 0xFE; + + return bits; +} + +static unsigned int read_index(unsigned char index) +{ + unsigned char *addr; + unsigned int value; + /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */ + addr = pci_addr_lin + ((index & 0xFF) << 2); + value = readl(addr); + return value; +} + +static void write_index(unsigned char index, unsigned int reg_val) +{ + unsigned char *addr; + addr = pci_addr_lin + ((index & 0xFF) << 2); + writel(reg_val, addr); +} + +MODULE_AUTHOR("Froenchenko Leonid"); +MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards"); +MODULE_LICENSE("GPL"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/staging/media/lirc/lirc_ene0100.h b/drivers/staging/media/lirc/lirc_ene0100.h new file mode 100644 index 000000000000..06bebd6acc46 --- /dev/null +++ b/drivers/staging/media/lirc/lirc_ene0100.h @@ -0,0 +1,169 @@ +/* + * driver for ENE KB3926 B/C/D CIR (also known as ENE0100) + * + * Copyright (C) 2009 Maxim Levitsky + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +/* hardware address */ +#define ENE_STATUS 0 /* hardware status - unused */ +#define ENE_ADDR_HI 1 /* hi byte of register address */ +#define ENE_ADDR_LO 2 /* low byte of register address */ +#define ENE_IO 3 /* read/write window */ +#define ENE_MAX_IO 4 + +/* 8 bytes of samples, divided in 2 halfs*/ +#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */ +#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */ +#define ENE_SAMPLE_VALUE_MASK 0x7F +#define ENE_SAMPLE_OVERFLOW 0x7F +#define ENE_SAMPLES_SIZE 4 + +/* fan input sample buffer */ +#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */ + /* each sample of normal buffer */ + +#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */ + /* if set, says that sample is pulse */ +#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */ + +/* first firmware register */ +#define ENE_FW1 0xF8F8 +#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */ +#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */ +#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */ +#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */ + +/* second firmware register */ +#define ENE_FW2 0xF8F9 +#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */ +#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */ +#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */ + /* learning input */ +#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */ +#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */ + +/* fan as input settings - only if learning capable */ +#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */ +#define ENE_FAN_AS_IN1_EN 0xCD +#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */ +#define ENE_FAN_AS_IN2_EN 0x03 +#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */ + +/* IRQ registers block (for revision B) */ +#define ENEB_IRQ 0xFD09 /* IRQ number */ +#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */ +#define ENEB_IRQ_STATUS 0xFD80 /* irq status */ +#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */ + +/* IRQ registers block (for revision C,D) */ +#define ENEC_IRQ 0xFE9B /* new irq settings register */ +#define ENEC_IRQ_MASK 0x0F /* irq number mask */ +#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */ +#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */ + +/* CIR block settings */ +#define ENE_CIR_CONF1 0xFEC0 +#define ENE_CIR_CONF1_ADC_ON 0x7 /* receiver on gpio40 enabled */ +#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */ +#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */ +#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */ + +#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */ +#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */ +#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */ + +#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */ +#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */ + + +/* transmitter - not implemented yet */ +/* KB3926C and higher */ +/* transmission is very similar to receiving, a byte is written to */ +/* ENE_TX_INPUT, in same manner as it is read from sample buffer */ +/* sample period is fixed*/ + + +/* transmitter ports */ +#define ENE_TX_PORT1 0xFC01 /* this enables one or both */ +#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */ +#define ENE_TX_PORT2 0xFC08 +#define ENE_TX_PORT2_EN (1 << 1) + +#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */ +#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */ +#define ENE_TX_UNK1 0xFECB /* set to 0x63 */ +#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */ + + +#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */ +#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */ +#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */ + +/* Hardware versions */ +#define ENE_HW_VERSION 0xFF00 /* hardware revision */ +#define ENE_HW_UNK 0xFF1D +#define ENE_HW_UNK_CLR (1 << 2) +#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */ +#define ENE_HW_VER_MINOR 0xFF1F +#define ENE_HW_VER_OLD 0xFD00 + +#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0)) + +#define ENE_DRIVER_NAME "enecir" +#define ENE_MAXGAP 250000 /* this is amount of time we wait + before turning the sampler, chosen + arbitry */ + +#define space(len) (-(len)) /* add a space */ + +/* software defines */ +#define ENE_IRQ_RX 1 +#define ENE_IRQ_TX 2 + +#define ENE_HW_B 1 /* 3926B */ +#define ENE_HW_C 2 /* 3926C */ +#define ENE_HW_D 3 /* 3926D */ + +#define ene_printk(level, text, ...) \ + printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__) + +struct ene_device { + struct pnp_dev *pnp_dev; + struct lirc_driver *lirc_driver; + + /* hw settings */ + unsigned long hw_io; + int irq; + + int hw_revision; /* hardware revision */ + int hw_learning_and_tx_capable; /* learning capable */ + int hw_gpio40_learning; /* gpio40 is learning */ + int hw_fan_as_normal_input; /* fan input is used as regular input */ + + /* device data */ + int idle; + int fan_input_inuse; + + int sample; + int in_use; + + struct timeval gap_start; +}; diff --git a/drivers/staging/media/lirc/lirc_igorplugusb.c b/drivers/staging/media/lirc/lirc_igorplugusb.c new file mode 100644 index 000000000000..0dc2c2b22c2b --- /dev/null +++ b/drivers/staging/media/lirc/lirc_igorplugusb.c @@ -0,0 +1,577 @@ +/* + * lirc_igorplugusb - USB remote support for LIRC + * + * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware. + * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm + * + * The device can only record bursts of up to 36 pulses/spaces. + * Works fine with RC5. Longer commands lead to device buffer overrun. + * (Maybe a better firmware or a microcontroller with more ram can help?) + * + * Version 0.1 [beta status] + * + * Copyright (C) 2004 Jan M. Hochstein + * + * + * This driver was derived from: + * Paul Miller + * "lirc_atiusb" module + * Vladimir Dergachev 's 2002 + * "USB ATI Remote support" (input device) + * Adrian Dewhurst 's 2002 + * "USB StreamZap remote driver" (LIRC) + * Artur Lipowski 's 2002 + * "lirc_dev" and "lirc_gpio" LIRC 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; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + + +/* module identification */ +#define DRIVER_VERSION "0.2" +#define DRIVER_AUTHOR \ + "Jan M. Hochstein " +#define DRIVER_DESC "Igorplug USB remote driver for LIRC" +#define DRIVER_NAME "lirc_igorplugusb" + +/* debugging support */ +#ifdef CONFIG_USB_DEBUG +static int debug = 1; +#else +static int debug; +#endif + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG fmt, ## args); \ + } while (0) + +/* One mode2 pulse/space has 4 bytes. */ +#define CODE_LENGTH sizeof(int) + +/* Igor's firmware cannot record bursts longer than 36. */ +#define DEVICE_BUFLEN 36 + +/* + * Header at the beginning of the device's buffer: + * unsigned char data_length + * unsigned char data_start (!=0 means ring-buffer overrun) + * unsigned char counter (incremented by each burst) + */ +#define DEVICE_HEADERLEN 3 + +/* This is for the gap */ +#define ADDITIONAL_LIRC_BYTES 2 + +/* times to poll per second */ +#define SAMPLE_RATE 100 +static int sample_rate = SAMPLE_RATE; + + +/**** Igor's USB Request Codes */ + +#define SET_INFRABUFFER_EMPTY 1 +/** + * Params: none + * Answer: empty + */ + +#define GET_INFRACODE 2 +/** + * Params: + * wValue: offset to begin reading infra buffer + * + * Answer: infra data + */ + +#define SET_DATAPORT_DIRECTION 3 +/** + * Params: + * wValue: (byte) 1 bit for each data port pin (0=in, 1=out) + * + * Answer: empty + */ + +#define GET_DATAPORT_DIRECTION 4 +/** + * Params: none + * + * Answer: (byte) 1 bit for each data port pin (0=in, 1=out) + */ + +#define SET_OUT_DATAPORT 5 +/** + * Params: + * wValue: byte to write to output data port + * + * Answer: empty + */ + +#define GET_OUT_DATAPORT 6 +/** + * Params: none + * + * Answer: least significant 3 bits read from output data port + */ + +#define GET_IN_DATAPORT 7 +/** + * Params: none + * + * Answer: least significant 3 bits read from input data port + */ + +#define READ_EEPROM 8 +/** + * Params: + * wValue: offset to begin reading EEPROM + * + * Answer: EEPROM bytes + */ + +#define WRITE_EEPROM 9 +/** + * Params: + * wValue: offset to EEPROM byte + * wIndex: byte to write + * + * Answer: empty + */ + +#define SEND_RS232 10 +/** + * Params: + * wValue: byte to send + * + * Answer: empty + */ + +#define RECV_RS232 11 +/** + * Params: none + * + * Answer: byte received + */ + +#define SET_RS232_BAUD 12 +/** + * Params: + * wValue: byte to write to UART bit rate register (UBRR) + * + * Answer: empty + */ + +#define GET_RS232_BAUD 13 +/** + * Params: none + * + * Answer: byte read from UART bit rate register (UBRR) + */ + + +/* data structure for each usb remote */ +struct igorplug { + + /* usb */ + struct usb_device *usbdev; + int devnum; + + unsigned char *buf_in; + unsigned int len_in; + int in_space; + struct timeval last_time; + + dma_addr_t dma_in; + + /* lirc */ + struct lirc_driver *d; + + /* handle sending (init strings) */ + int send_flags; +}; + +static int unregister_from_lirc(struct igorplug *ir) +{ + struct lirc_driver *d; + int devnum; + + if (!ir) { + printk(KERN_ERR "%s: called with NULL device struct!\n", + __func__); + return -EINVAL; + } + + devnum = ir->devnum; + d = ir->d; + + if (!d) { + printk(KERN_ERR "%s: called with NULL lirc driver struct!\n", + __func__); + return -EINVAL; + } + + dprintk(DRIVER_NAME "[%d]: calling lirc_unregister_driver\n", devnum); + lirc_unregister_driver(d->minor); + + kfree(d); + ir->d = NULL; + kfree(ir); + + return devnum; +} + +static int set_use_inc(void *data) +{ + struct igorplug *ir = data; + + if (!ir) { + printk(DRIVER_NAME "[?]: set_use_inc called with no context\n"); + return -EIO; + } + + dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum); + + if (!ir->usbdev) + return -ENODEV; + + return 0; +} + +static void set_use_dec(void *data) +{ + struct igorplug *ir = data; + + if (!ir) { + printk(DRIVER_NAME "[?]: set_use_dec called with no context\n"); + return; + } + + dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum); +} + +static void send_fragment(struct igorplug *ir, struct lirc_buffer *buf, + int i, int max) +{ + int code; + + /* MODE2: pulse/space (PULSE_BIT) in 1us units */ + while (i < max) { + /* 1 Igor-tick = 85.333333 us */ + code = (unsigned int)ir->buf_in[i] * 85 + + (unsigned int)ir->buf_in[i] / 3; + ir->last_time.tv_usec += code; + if (ir->in_space) + code |= PULSE_BIT; + lirc_buffer_write(buf, (unsigned char *)&code); + /* 1 chunk = CODE_LENGTH bytes */ + ir->in_space ^= 1; + ++i; + } +} + +/** + * Called in user context. + * return 0 if data was added to the buffer and + * -ENODATA if none was available. This should add some number of bits + * evenly divisible by code_length to the buffer + */ +static int igorplugusb_remote_poll(void *data, struct lirc_buffer *buf) +{ + int ret; + struct igorplug *ir = (struct igorplug *)data; + + if (!ir || !ir->usbdev) /* Has the device been removed? */ + return -ENODEV; + + memset(ir->buf_in, 0, ir->len_in); + + ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), + GET_INFRACODE, USB_TYPE_VENDOR | USB_DIR_IN, + 0/* offset */, /*unused*/0, + ir->buf_in, ir->len_in, + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); + if (ret > 0) { + int code, timediff; + struct timeval now; + + /* ACK packet has 1 byte --> ignore */ + if (ret < DEVICE_HEADERLEN) + return -ENODATA; + + dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n", + ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]); + + do_gettimeofday(&now); + timediff = now.tv_sec - ir->last_time.tv_sec; + if (timediff + 1 > PULSE_MASK / 1000000) + timediff = PULSE_MASK; + else { + timediff *= 1000000; + timediff += now.tv_usec - ir->last_time.tv_usec; + } + ir->last_time.tv_sec = now.tv_sec; + ir->last_time.tv_usec = now.tv_usec; + + /* create leading gap */ + code = timediff; + lirc_buffer_write(buf, (unsigned char *)&code); + ir->in_space = 1; /* next comes a pulse */ + + if (ir->buf_in[2] == 0) + send_fragment(ir, buf, DEVICE_HEADERLEN, ret); + else { + printk(KERN_WARNING DRIVER_NAME + "[%d]: Device buffer overrun.\n", ir->devnum); + /* HHHNNNNNNNNNNNOOOOOOOO H = header + <---[2]---> N = newer + <---------ret--------> O = older */ + ir->buf_in[2] %= ret - DEVICE_HEADERLEN; /* sanitize */ + /* keep even-ness to not desync pulse/pause */ + send_fragment(ir, buf, DEVICE_HEADERLEN + + ir->buf_in[2] - (ir->buf_in[2] & 1), ret); + send_fragment(ir, buf, DEVICE_HEADERLEN, + DEVICE_HEADERLEN + ir->buf_in[2]); + } + + ret = usb_control_msg( + ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), + SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, + /*unused*/0, /*unused*/0, + /*dummy*/ir->buf_in, /*dummy*/ir->len_in, + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); + if (ret < 0) + printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: " + "error %d\n", ir->devnum, ret); + return 0; + } else if (ret < 0) + printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n", + ir->devnum, ret); + + return -ENODATA; +} + + + +static int igorplugusb_remote_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = NULL; + struct usb_host_interface *idesc = NULL; + struct usb_endpoint_descriptor *ep; + struct igorplug *ir = NULL; + struct lirc_driver *driver = NULL; + int devnum, pipe, maxp; + int minor = 0; + char buf[63], name[128] = ""; + int mem_failure = 0; + int ret; + + dprintk(DRIVER_NAME ": usb probe called.\n"); + + dev = interface_to_usbdev(intf); + + idesc = intf->cur_altsetting; + + if (idesc->desc.bNumEndpoints != 1) + return -ENODEV; + + ep = &idesc->endpoint->desc; + if (((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK) + != USB_DIR_IN) + || (ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + != USB_ENDPOINT_XFER_CONTROL) + return -ENODEV; + + pipe = usb_rcvctrlpipe(dev, ep->bEndpointAddress); + devnum = dev->devnum; + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + dprintk(DRIVER_NAME "[%d]: bytes_in_key=%zu maxp=%d\n", + devnum, CODE_LENGTH, maxp); + + mem_failure = 0; + ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL); + if (!ir) { + mem_failure = 1; + goto mem_failure_switch; + } + driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!driver) { + mem_failure = 2; + goto mem_failure_switch; + } + + ir->buf_in = usb_alloc_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, + GFP_ATOMIC, &ir->dma_in); + if (!ir->buf_in) { + mem_failure = 3; + goto mem_failure_switch; + } + + strcpy(driver->name, DRIVER_NAME " "); + driver->minor = -1; + driver->code_length = CODE_LENGTH * 8; /* in bits */ + driver->features = LIRC_CAN_REC_MODE2; + driver->data = ir; + driver->chunk_size = CODE_LENGTH; + driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES; + driver->set_use_inc = &set_use_inc; + driver->set_use_dec = &set_use_dec; + driver->sample_rate = sample_rate; /* per second */ + driver->add_to_buf = &igorplugusb_remote_poll; + driver->dev = &intf->dev; + driver->owner = THIS_MODULE; + + minor = lirc_register_driver(driver); + if (minor < 0) + mem_failure = 9; + +mem_failure_switch: + + switch (mem_failure) { + case 9: + usb_free_coherent(dev, DEVICE_BUFLEN + DEVICE_HEADERLEN, + ir->buf_in, ir->dma_in); + case 3: + kfree(driver); + case 2: + kfree(ir); + case 1: + printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n", + devnum, mem_failure); + return -ENOMEM; + } + + driver->minor = minor; + ir->d = driver; + ir->devnum = devnum; + ir->usbdev = dev; + ir->len_in = DEVICE_BUFLEN + DEVICE_HEADERLEN; + ir->in_space = 1; /* First mode2 event is a space. */ + do_gettimeofday(&ir->last_time); + + if (dev->descriptor.iManufacturer + && usb_string(dev, dev->descriptor.iManufacturer, + buf, sizeof(buf)) > 0) + strlcpy(name, buf, sizeof(name)); + if (dev->descriptor.iProduct + && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0) + snprintf(name + strlen(name), sizeof(name) - strlen(name), + " %s", buf); + printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name, + dev->bus->busnum, devnum); + + /* clear device buffer */ + ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0), + SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN, + /*unused*/0, /*unused*/0, + /*dummy*/ir->buf_in, /*dummy*/ir->len_in, + /*timeout*/HZ * USB_CTRL_GET_TIMEOUT); + if (ret < 0) + printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n", + devnum, ret); + + usb_set_intfdata(intf, ir); + return 0; +} + + +static void igorplugusb_remote_disconnect(struct usb_interface *intf) +{ + struct usb_device *usbdev = interface_to_usbdev(intf); + struct igorplug *ir = usb_get_intfdata(intf); + struct device *dev = &intf->dev; + int devnum; + + usb_set_intfdata(intf, NULL); + + if (!ir || !ir->d) + return; + + ir->usbdev = NULL; + + usb_free_coherent(usbdev, ir->len_in, ir->buf_in, ir->dma_in); + + devnum = unregister_from_lirc(ir); + + dev_info(dev, DRIVER_NAME "[%d]: %s done\n", devnum, __func__); +} + +static struct usb_device_id igorplugusb_remote_id_table[] = { + /* Igor Plug USB (Atmel's Manufact. ID) */ + { USB_DEVICE(0x03eb, 0x0002) }, + /* Fit PC2 Infrared Adapter */ + { USB_DEVICE(0x03eb, 0x21fe) }, + + /* Terminating entry */ + { } +}; + +static struct usb_driver igorplugusb_remote_driver = { + .name = DRIVER_NAME, + .probe = igorplugusb_remote_probe, + .disconnect = igorplugusb_remote_disconnect, + .id_table = igorplugusb_remote_id_table +}; + +static int __init igorplugusb_remote_init(void) +{ + int ret = 0; + + dprintk(DRIVER_NAME ": loaded, debug mode enabled\n"); + + ret = usb_register(&igorplugusb_remote_driver); + if (ret) + printk(KERN_ERR DRIVER_NAME ": usb register failed!\n"); + + return ret; +} + +static void __exit igorplugusb_remote_exit(void) +{ + usb_deregister(&igorplugusb_remote_driver); +} + +module_init(igorplugusb_remote_init); +module_exit(igorplugusb_remote_exit); + +#include +MODULE_INFO(vermagic, VERMAGIC_STRING); + +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, igorplugusb_remote_id_table); + +module_param(sample_rate, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug enabled or not"); diff --git a/drivers/staging/media/lirc/lirc_imon.c b/drivers/staging/media/lirc/lirc_imon.c new file mode 100644 index 000000000000..f5308d5929c6 --- /dev/null +++ b/drivers/staging/media/lirc/lirc_imon.c @@ -0,0 +1,1050 @@ +/* + * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD + * including the iMON PAD model + * + * Copyright(C) 2004 Venky Raju(dev@venky.ws) + * Copyright(C) 2009 Jarod Wilson + * + * lirc_imon is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU 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 + + +#define MOD_AUTHOR "Venky Raju " +#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display" +#define MOD_NAME "lirc_imon" +#define MOD_VERSION "0.8" + +#define DISPLAY_MINOR_BASE 144 +#define DEVICE_NAME "lcd%d" + +#define BUF_CHUNK_SIZE 4 +#define BUF_SIZE 128 + +#define BIT_DURATION 250 /* each bit received is 250us */ + +/*** P R O T O T Y P E S ***/ + +/* USB Callback prototypes */ +static int imon_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void imon_disconnect(struct usb_interface *interface); +static void usb_rx_callback(struct urb *urb); +static void usb_tx_callback(struct urb *urb); + +/* suspend/resume support */ +static int imon_resume(struct usb_interface *intf); +static int imon_suspend(struct usb_interface *intf, pm_message_t message); + +/* Display file_operations function prototypes */ +static int display_open(struct inode *inode, struct file *file); +static int display_close(struct inode *inode, struct file *file); + +/* VFD write operation */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos); + +/* LIRC driver function prototypes */ +static int ir_open(void *data); +static void ir_close(void *data); + +/* Driver init/exit prototypes */ +static int __init imon_init(void); +static void __exit imon_exit(void); + +/*** G L O B A L S ***/ +#define IMON_DATA_BUF_SZ 35 + +struct imon_context { + struct usb_device *usbdev; + /* Newer devices have two interfaces */ + int display; /* not all controllers do */ + int display_isopen; /* display port has been opened */ + int ir_isopen; /* IR port open */ + int dev_present; /* USB device presence */ + struct mutex ctx_lock; /* to lock this object */ + wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ + + int vfd_proto_6p; /* some VFD require a 6th packet */ + + struct lirc_driver *driver; + struct usb_endpoint_descriptor *rx_endpoint; + struct usb_endpoint_descriptor *tx_endpoint; + struct urb *rx_urb; + struct urb *tx_urb; + unsigned char usb_rx_buf[8]; + unsigned char usb_tx_buf[8]; + + struct rx_data { + int count; /* length of 0 or 1 sequence */ + int prev_bit; /* logic level of sequence */ + int initial_space; /* initial space flag */ + } rx; + + struct tx_t { + unsigned char data_buf[IMON_DATA_BUF_SZ]; /* user data buffer */ + struct completion finished; /* wait for write to finish */ + atomic_t busy; /* write in progress */ + int status; /* status of tx completion */ + } tx; +}; + +static const struct file_operations display_fops = { + .owner = THIS_MODULE, + .open = &display_open, + .write = &vfd_write, + .release = &display_close, + .llseek = noop_llseek, +}; + +/* + * USB Device ID for iMON USB Control Boards + * + * The Windows drivers contain 6 different inf files, more or less one for + * each new device until the 0x0034-0x0046 devices, which all use the same + * driver. Some of the devices in the 34-46 range haven't been definitively + * identified yet. Early devices have either a TriGem Computer, Inc. or a + * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later + * devices use the SoundGraph vendor ID (0x15c2). + */ +static struct usb_device_id imon_usb_id_table[] = { + /* TriGem iMON (IR only) -- TG_iMON.inf */ + { USB_DEVICE(0x0aa8, 0x8001) }, + + /* SoundGraph iMON (IR only) -- sg_imon.inf */ + { USB_DEVICE(0x04e8, 0xff30) }, + + /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */ + { USB_DEVICE(0x0aa8, 0xffda) }, + + /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */ + { USB_DEVICE(0x15c2, 0xffda) }, + + {} +}; + +/* Some iMON VFD models requires a 6th packet for VFD writes */ +static struct usb_device_id vfd_proto_6p_list[] = { + { USB_DEVICE(0x15c2, 0xffda) }, + {} +}; + +/* Some iMON devices have no lcd/vfd, don't set one up */ +static struct usb_device_id ir_only_list[] = { + { USB_DEVICE(0x0aa8, 0x8001) }, + { USB_DEVICE(0x04e8, 0xff30) }, + {} +}; + +/* USB Device data */ +static struct usb_driver imon_driver = { + .name = MOD_NAME, + .probe = imon_probe, + .disconnect = imon_disconnect, + .suspend = imon_suspend, + .resume = imon_resume, + .id_table = imon_usb_id_table, +}; + +static struct usb_class_driver imon_class = { + .name = DEVICE_NAME, + .fops = &display_fops, + .minor_base = DISPLAY_MINOR_BASE, +}; + +/* to prevent races between open() and disconnect(), probing, etc */ +static DEFINE_MUTEX(driver_lock); + +static int debug; + +/*** M O D U L E C O D E ***/ + +MODULE_AUTHOR(MOD_AUTHOR); +MODULE_DESCRIPTION(MOD_DESC); +MODULE_VERSION(MOD_VERSION); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(usb, imon_usb_id_table); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)"); + +static void free_imon_context(struct imon_context *context) +{ + struct device *dev = context->driver->dev; + usb_free_urb(context->tx_urb); + usb_free_urb(context->rx_urb); + lirc_buffer_free(context->driver->rbuf); + kfree(context->driver->rbuf); + kfree(context->driver); + kfree(context); + + dev_dbg(dev, "%s: iMON context freed\n", __func__); +} + +static void deregister_from_lirc(struct imon_context *context) +{ + int retval; + int minor = context->driver->minor; + + retval = lirc_unregister_driver(minor); + if (retval) + err("%s: unable to deregister from lirc(%d)", + __func__, retval); + else + printk(KERN_INFO MOD_NAME ": Deregistered iMON driver " + "(minor:%d)\n", minor); + +} + +/** + * Called when the Display device (e.g. /dev/lcd0) + * is opened by the application. + */ +static int display_open(struct inode *inode, struct file *file) +{ + struct usb_interface *interface; + struct imon_context *context = NULL; + int subminor; + int retval = 0; + + /* prevent races with disconnect */ + mutex_lock(&driver_lock); + + subminor = iminor(inode); + interface = usb_find_interface(&imon_driver, subminor); + if (!interface) { + err("%s: could not find interface for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + context = usb_get_intfdata(interface); + + if (!context) { + err("%s: no context found for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + + mutex_lock(&context->ctx_lock); + + if (!context->display) { + err("%s: display not supported by device", __func__); + retval = -ENODEV; + } else if (context->display_isopen) { + err("%s: display port is already open", __func__); + retval = -EBUSY; + } else { + context->display_isopen = 1; + file->private_data = context; + dev_info(context->driver->dev, "display port opened\n"); + } + + mutex_unlock(&context->ctx_lock); + +exit: + mutex_unlock(&driver_lock); + return retval; +} + +/** + * Called when the display device (e.g. /dev/lcd0) + * is closed by the application. + */ +static int display_close(struct inode *inode, struct file *file) +{ + struct imon_context *context = NULL; + int retval = 0; + + context = file->private_data; + + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->display) { + err("%s: display not supported by device", __func__); + retval = -ENODEV; + } else if (!context->display_isopen) { + err("%s: display is not open", __func__); + retval = -EIO; + } else { + context->display_isopen = 0; + dev_info(context->driver->dev, "display port closed\n"); + if (!context->dev_present && !context->ir_isopen) { + /* + * Device disconnected before close and IR port is not + * open. If IR port is open, context will be deleted by + * ir_close. + */ + mutex_unlock(&context->ctx_lock); + free_imon_context(context); + return retval; + } + } + + mutex_unlock(&context->ctx_lock); + return retval; +} + +/** + * Sends a packet to the device -- this function must be called + * with context->ctx_lock held. + */ +static int send_packet(struct imon_context *context) +{ + unsigned int pipe; + int interval = 0; + int retval = 0; + + /* Check if we need to use control or interrupt urb */ + pipe = usb_sndintpipe(context->usbdev, + context->tx_endpoint->bEndpointAddress); + interval = context->tx_endpoint->bInterval; + + usb_fill_int_urb(context->tx_urb, context->usbdev, pipe, + context->usb_tx_buf, + sizeof(context->usb_tx_buf), + usb_tx_callback, context, interval); + + context->tx_urb->actual_length = 0; + + init_completion(&context->tx.finished); + atomic_set(&(context->tx.busy), 1); + + retval = usb_submit_urb(context->tx_urb, GFP_KERNEL); + if (retval) { + atomic_set(&(context->tx.busy), 0); + err("%s: error submitting urb(%d)", __func__, retval); + } else { + /* Wait for transmission to complete (or abort) */ + mutex_unlock(&context->ctx_lock); + retval = wait_for_completion_interruptible( + &context->tx.finished); + if (retval) + err("%s: task interrupted", __func__); + mutex_lock(&context->ctx_lock); + + retval = context->tx.status; + if (retval) + err("%s: packet tx failed (%d)", __func__, retval); + } + + return retval; +} + +/** + * Writes data to the VFD. The iMON VFD is 2x16 characters + * and requires data in 5 consecutive USB interrupt packets, + * each packet but the last carrying 7 bytes. + * + * I don't know if the VFD board supports features such as + * scrolling, clearing rows, blanking, etc. so at + * the caller must provide a full screen of data. If fewer + * than 32 bytes are provided spaces will be appended to + * generate a full screen. + */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos) +{ + int i; + int offset; + int seq; + int retval = 0; + struct imon_context *context; + const unsigned char vfd_packet6[] = { + 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF }; + int *data_buf = NULL; + + context = file->private_data; + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->dev_present) { + err("%s: no iMON device present", __func__); + retval = -ENODEV; + goto exit; + } + + if (n_bytes <= 0 || n_bytes > IMON_DATA_BUF_SZ - 3) { + err("%s: invalid payload size", __func__); + retval = -EINVAL; + goto exit; + } + + data_buf = memdup_user(buf, n_bytes); + if (IS_ERR(data_buf)) { + retval = PTR_ERR(data_buf); + goto exit; + } + + memcpy(context->tx.data_buf, data_buf, n_bytes); + + /* Pad with spaces */ + for (i = n_bytes; i < IMON_DATA_BUF_SZ - 3; ++i) + context->tx.data_buf[i] = ' '; + + for (i = IMON_DATA_BUF_SZ - 3; i < IMON_DATA_BUF_SZ; ++i) + context->tx.data_buf[i] = 0xFF; + + offset = 0; + seq = 0; + + do { + memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7); + context->usb_tx_buf[7] = (unsigned char) seq; + + retval = send_packet(context); + if (retval) { + err("%s: send packet failed for packet #%d", + __func__, seq/2); + goto exit; + } else { + seq += 2; + offset += 7; + } + + } while (offset < IMON_DATA_BUF_SZ); + + if (context->vfd_proto_6p) { + /* Send packet #6 */ + memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6)); + context->usb_tx_buf[7] = (unsigned char) seq; + retval = send_packet(context); + if (retval) + err("%s: send packet failed for packet #%d", + __func__, seq/2); + } + +exit: + mutex_unlock(&context->ctx_lock); + kfree(data_buf); + + return (!retval) ? n_bytes : retval; +} + +/** + * Callback function for USB core API: transmit data + */ +static void usb_tx_callback(struct urb *urb) +{ + struct imon_context *context; + + if (!urb) + return; + context = (struct imon_context *)urb->context; + if (!context) + return; + + context->tx.status = urb->status; + + /* notify waiters that write has finished */ + atomic_set(&context->tx.busy, 0); + complete(&context->tx.finished); + + return; +} + +/** + * Called by lirc_dev when the application opens /dev/lirc + */ +static int ir_open(void *data) +{ + int retval = 0; + struct imon_context *context; + + /* prevent races with disconnect */ + mutex_lock(&driver_lock); + + context = (struct imon_context *)data; + + /* initial IR protocol decode variables */ + context->rx.count = 0; + context->rx.initial_space = 1; + context->rx.prev_bit = 0; + + context->ir_isopen = 1; + dev_info(context->driver->dev, "IR port opened\n"); + + mutex_unlock(&driver_lock); + return retval; +} + +/** + * Called by lirc_dev when the application closes /dev/lirc + */ +static void ir_close(void *data) +{ + struct imon_context *context; + + context = (struct imon_context *)data; + if (!context) { + err("%s: no context for device", __func__); + return; + } + + mutex_lock(&context->ctx_lock); + + context->ir_isopen = 0; + dev_info(context->driver->dev, "IR port closed\n"); + + if (!context->dev_present) { + /* + * Device disconnected while IR port was still open. Driver + * was not deregistered at disconnect time, so do it now. + */ + deregister_from_lirc(context); + + if (!context->display_isopen) { + mutex_unlock(&context->ctx_lock); + free_imon_context(context); + return; + } + /* + * If display port is open, context will be deleted by + * display_close + */ + } + + mutex_unlock(&context->ctx_lock); + return; +} + +/** + * Convert bit count to time duration (in us) and submit + * the value to lirc_dev. + */ +static void submit_data(struct imon_context *context) +{ + unsigned char buf[4]; + int value = context->rx.count; + int i; + + dev_dbg(context->driver->dev, "submitting data to LIRC\n"); + + value *= BIT_DURATION; + value &= PULSE_MASK; + if (context->rx.prev_bit) + value |= PULSE_BIT; + + for (i = 0; i < 4; ++i) + buf[i] = value>>(i*8); + + lirc_buffer_write(context->driver->rbuf, buf); + wake_up(&context->driver->rbuf->wait_poll); + return; +} + +static inline int tv2int(const struct timeval *a, const struct timeval *b) +{ + int usecs = 0; + int sec = 0; + + if (b->tv_usec > a->tv_usec) { + usecs = 1000000; + sec--; + } + + usecs += a->tv_usec - b->tv_usec; + + sec += a->tv_sec - b->tv_sec; + sec *= 1000; + usecs /= 1000; + sec += usecs; + + if (sec < 0) + sec = 1000; + + return sec; +} + +/** + * Process the incoming packet + */ +static void imon_incoming_packet(struct imon_context *context, + struct urb *urb, int intf) +{ + int len = urb->actual_length; + unsigned char *buf = urb->transfer_buffer; + struct device *dev = context->driver->dev; + int octet, bit; + unsigned char mask; + int i; + + /* + * just bail out if no listening IR client + */ + if (!context->ir_isopen) + return; + + if (len != 8) { + dev_warn(dev, "imon %s: invalid incoming packet " + "size (len = %d, intf%d)\n", __func__, len, intf); + return; + } + + if (debug) { + printk(KERN_INFO "raw packet: "); + for (i = 0; i < len; ++i) + printk("%02x ", buf[i]); + printk("\n"); + } + + /* + * Translate received data to pulse and space lengths. + * Received data is active low, i.e. pulses are 0 and + * spaces are 1. + * + * My original algorithm was essentially similar to + * Changwoo Ryu's with the exception that he switched + * the incoming bits to active high and also fed an + * initial space to LIRC at the start of a new sequence + * if the previous bit was a pulse. + * + * I've decided to adopt his algorithm. + */ + + if (buf[7] == 1 && context->rx.initial_space) { + /* LIRC requires a leading space */ + context->rx.prev_bit = 0; + context->rx.count = 4; + submit_data(context); + context->rx.count = 0; + } + + for (octet = 0; octet < 5; ++octet) { + mask = 0x80; + for (bit = 0; bit < 8; ++bit) { + int curr_bit = !(buf[octet] & mask); + if (curr_bit != context->rx.prev_bit) { + if (context->rx.count) { + submit_data(context); + context->rx.count = 0; + } + context->rx.prev_bit = curr_bit; + } + ++context->rx.count; + mask >>= 1; + } + } + + if (buf[7] == 10) { + if (context->rx.count) { + submit_data(context); + context->rx.count = 0; + } + context->rx.initial_space = context->rx.prev_bit; + } +} + +/** + * Callback function for USB core API: receive data + */ +static void usb_rx_callback(struct urb *urb) +{ + struct imon_context *context; + int intfnum = 0; + + if (!urb) + return; + + context = (struct imon_context *)urb->context; + if (!context) + return; + + switch (urb->status) { + case -ENOENT: /* usbcore unlink successful! */ + return; + + case 0: + imon_incoming_packet(context, urb, intfnum); + break; + + default: + dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n", + __func__, urb->status); + break; + } + + usb_submit_urb(context->rx_urb, GFP_ATOMIC); + + return; +} + +/** + * Callback function for USB core API: Probe + */ +static int imon_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev = NULL; + struct usb_host_interface *iface_desc = NULL; + struct usb_endpoint_descriptor *rx_endpoint = NULL; + struct usb_endpoint_descriptor *tx_endpoint = NULL; + struct urb *rx_urb = NULL; + struct urb *tx_urb = NULL; + struct lirc_driver *driver = NULL; + struct lirc_buffer *rbuf = NULL; + struct device *dev = &interface->dev; + int ifnum; + int lirc_minor = 0; + int num_endpts; + int retval = 0; + int display_ep_found = 0; + int ir_ep_found = 0; + int alloc_status = 0; + int vfd_proto_6p = 0; + struct imon_context *context = NULL; + int i; + u16 vendor, product; + + /* prevent races probing devices w/multiple interfaces */ + mutex_lock(&driver_lock); + + context = kzalloc(sizeof(struct imon_context), GFP_KERNEL); + if (!context) { + err("%s: kzalloc failed for context", __func__); + alloc_status = 1; + goto alloc_status_switch; + } + + /* + * Try to auto-detect the type of display if the user hasn't set + * it by hand via the display_type modparam. Default is VFD. + */ + if (usb_match_id(interface, ir_only_list)) + context->display = 0; + else + context->display = 1; + + usbdev = usb_get_dev(interface_to_usbdev(interface)); + iface_desc = interface->cur_altsetting; + num_endpts = iface_desc->desc.bNumEndpoints; + ifnum = iface_desc->desc.bInterfaceNumber; + vendor = le16_to_cpu(usbdev->descriptor.idVendor); + product = le16_to_cpu(usbdev->descriptor.idProduct); + + dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n", + __func__, vendor, product, ifnum); + + /* + * Scan the endpoint list and set: + * first input endpoint = IR endpoint + * first output endpoint = display endpoint + */ + for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) { + struct usb_endpoint_descriptor *ep; + int ep_dir; + int ep_type; + ep = &iface_desc->endpoint[i].desc; + ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; + ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (!ir_ep_found && + ep_dir == USB_DIR_IN && + ep_type == USB_ENDPOINT_XFER_INT) { + + rx_endpoint = ep; + ir_ep_found = 1; + dev_dbg(dev, "%s: found IR endpoint\n", __func__); + + } else if (!display_ep_found && ep_dir == USB_DIR_OUT && + ep_type == USB_ENDPOINT_XFER_INT) { + tx_endpoint = ep; + display_ep_found = 1; + dev_dbg(dev, "%s: found display endpoint\n", __func__); + } + } + + /* + * Some iMON receivers have no display. Unfortunately, it seems + * that SoundGraph recycles device IDs between devices both with + * and without... :\ + */ + if (context->display == 0) { + display_ep_found = 0; + dev_dbg(dev, "%s: device has no display\n", __func__); + } + + /* Input endpoint is mandatory */ + if (!ir_ep_found) { + err("%s: no valid input (IR) endpoint found.", __func__); + retval = -ENODEV; + alloc_status = 2; + goto alloc_status_switch; + } + + /* Determine if display requires 6 packets */ + if (display_ep_found) { + if (usb_match_id(interface, vfd_proto_6p_list)) + vfd_proto_6p = 1; + + dev_dbg(dev, "%s: vfd_proto_6p: %d\n", + __func__, vfd_proto_6p); + } + + driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!driver) { + err("%s: kzalloc failed for lirc_driver", __func__); + alloc_status = 2; + goto alloc_status_switch; + } + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!rbuf) { + err("%s: kmalloc failed for lirc_buffer", __func__); + alloc_status = 3; + goto alloc_status_switch; + } + if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { + err("%s: lirc_buffer_init failed", __func__); + alloc_status = 4; + goto alloc_status_switch; + } + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) { + err("%s: usb_alloc_urb failed for IR urb", __func__); + alloc_status = 5; + goto alloc_status_switch; + } + tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tx_urb) { + err("%s: usb_alloc_urb failed for display urb", + __func__); + alloc_status = 6; + goto alloc_status_switch; + } + + mutex_init(&context->ctx_lock); + context->vfd_proto_6p = vfd_proto_6p; + + strcpy(driver->name, MOD_NAME); + driver->minor = -1; + driver->code_length = BUF_CHUNK_SIZE * 8; + driver->sample_rate = 0; + driver->features = LIRC_CAN_REC_MODE2; + driver->data = context; + driver->rbuf = rbuf; + driver->set_use_inc = ir_open; + driver->set_use_dec = ir_close; + driver->dev = &interface->dev; + driver->owner = THIS_MODULE; + + mutex_lock(&context->ctx_lock); + + context->driver = driver; + /* start out in keyboard mode */ + + lirc_minor = lirc_register_driver(driver); + if (lirc_minor < 0) { + err("%s: lirc_register_driver failed", __func__); + alloc_status = 7; + goto unlock; + } else + dev_info(dev, "Registered iMON driver " + "(lirc minor: %d)\n", lirc_minor); + + /* Needed while unregistering! */ + driver->minor = lirc_minor; + + context->usbdev = usbdev; + context->dev_present = 1; + context->rx_endpoint = rx_endpoint; + context->rx_urb = rx_urb; + + /* + * tx is used to send characters to lcd/vfd, associate RF + * remotes, set IR protocol, and maybe more... + */ + context->tx_endpoint = tx_endpoint; + context->tx_urb = tx_urb; + + if (display_ep_found) + context->display = 1; + + usb_fill_int_urb(context->rx_urb, context->usbdev, + usb_rcvintpipe(context->usbdev, + context->rx_endpoint->bEndpointAddress), + context->usb_rx_buf, sizeof(context->usb_rx_buf), + usb_rx_callback, context, + context->rx_endpoint->bInterval); + + retval = usb_submit_urb(context->rx_urb, GFP_KERNEL); + + if (retval) { + err("%s: usb_submit_urb failed for intf0 (%d)", + __func__, retval); + mutex_unlock(&context->ctx_lock); + goto exit; + } + + usb_set_intfdata(interface, context); + + if (context->display && ifnum == 0) { + dev_dbg(dev, "%s: Registering iMON display with sysfs\n", + __func__); + + if (usb_register_dev(interface, &imon_class)) { + /* Not a fatal error, so ignore */ + dev_info(dev, "%s: could not get a minor number for " + "display\n", __func__); + } + } + + dev_info(dev, "iMON device (%04x:%04x, intf%d) on " + "usb<%d:%d> initialized\n", vendor, product, ifnum, + usbdev->bus->busnum, usbdev->devnum); + +unlock: + mutex_unlock(&context->ctx_lock); +alloc_status_switch: + + switch (alloc_status) { + case 7: + usb_free_urb(tx_urb); + case 6: + usb_free_urb(rx_urb); + case 5: + if (rbuf) + lirc_buffer_free(rbuf); + case 4: + kfree(rbuf); + case 3: + kfree(driver); + case 2: + kfree(context); + context = NULL; + case 1: + if (retval != -ENODEV) + retval = -ENOMEM; + break; + case 0: + retval = 0; + } + +exit: + mutex_unlock(&driver_lock); + + return retval; +} + +/** + * Callback function for USB core API: disconnect + */ +static void imon_disconnect(struct usb_interface *interface) +{ + struct imon_context *context; + int ifnum; + + /* prevent races with ir_open()/display_open() */ + mutex_lock(&driver_lock); + + context = usb_get_intfdata(interface); + ifnum = interface->cur_altsetting->desc.bInterfaceNumber; + + mutex_lock(&context->ctx_lock); + + usb_set_intfdata(interface, NULL); + + /* Abort ongoing write */ + if (atomic_read(&context->tx.busy)) { + usb_kill_urb(context->tx_urb); + complete_all(&context->tx.finished); + } + + context->dev_present = 0; + usb_kill_urb(context->rx_urb); + if (context->display) + usb_deregister_dev(interface, &imon_class); + + if (!context->ir_isopen && !context->dev_present) { + deregister_from_lirc(context); + mutex_unlock(&context->ctx_lock); + if (!context->display_isopen) + free_imon_context(context); + } else + mutex_unlock(&context->ctx_lock); + + mutex_unlock(&driver_lock); + + printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n", + __func__, ifnum); +} + +static int imon_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct imon_context *context = usb_get_intfdata(intf); + + usb_kill_urb(context->rx_urb); + + return 0; +} + +static int imon_resume(struct usb_interface *intf) +{ + int rc = 0; + struct imon_context *context = usb_get_intfdata(intf); + + usb_fill_int_urb(context->rx_urb, context->usbdev, + usb_rcvintpipe(context->usbdev, + context->rx_endpoint->bEndpointAddress), + context->usb_rx_buf, sizeof(context->usb_rx_buf), + usb_rx_callback, context, + context->rx_endpoint->bInterval); + + rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC); + + return rc; +} + +static int __init imon_init(void) +{ + int rc; + + printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n"); + + rc = usb_register(&imon_driver); + if (rc) { + err("%s: usb register failed(%d)", __func__, rc); + return -ENODEV; + } + + return 0; +} + +static void __exit imon_exit(void) +{ + usb_deregister(&imon_driver); + printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n"); +} + +module_init(imon_init); +module_exit(imon_exit); diff --git a/drivers/staging/media/lirc/lirc_parallel.c b/drivers/staging/media/lirc/lirc_parallel.c new file mode 100644 index 000000000000..792aac0a8e7b --- /dev/null +++ b/drivers/staging/media/lirc/lirc_parallel.c @@ -0,0 +1,755 @@ +/* + * lirc_parallel.c + * + * lirc_parallel - device driver for infra-red signal receiving and + * transmitting unit built by the author + * + * Copyright (C) 1998 Christoph Bartelmus + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/*** Includes ***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include "lirc_parallel.h" + +#define LIRC_DRIVER_NAME "lirc_parallel" + +#ifndef LIRC_IRQ +#define LIRC_IRQ 7 +#endif +#ifndef LIRC_PORT +#define LIRC_PORT 0x378 +#endif +#ifndef LIRC_TIMER +#define LIRC_TIMER 65536 +#endif + +/*** Global Variables ***/ + +static int debug; +static int check_pselecd; + +unsigned int irq = LIRC_IRQ; +unsigned int io = LIRC_PORT; +#ifdef LIRC_TIMER +unsigned int timer; +unsigned int default_timer = LIRC_TIMER; +#endif + +#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */ + +static int rbuf[RBUF_SIZE]; + +DECLARE_WAIT_QUEUE_HEAD(lirc_wait); + +unsigned int rptr; +unsigned int wptr; +unsigned int lost_irqs; +int is_open; + +struct parport *pport; +struct pardevice *ppdevice; +int is_claimed; + +unsigned int tx_mask = 1; + +/*** Internal Functions ***/ + +static unsigned int in(int offset) +{ + switch (offset) { + case LIRC_LP_BASE: + return parport_read_data(pport); + case LIRC_LP_STATUS: + return parport_read_status(pport); + case LIRC_LP_CONTROL: + return parport_read_control(pport); + } + return 0; /* make compiler happy */ +} + +static void out(int offset, int value) +{ + switch (offset) { + case LIRC_LP_BASE: + parport_write_data(pport, value); + break; + case LIRC_LP_CONTROL: + parport_write_control(pport, value); + break; + case LIRC_LP_STATUS: + printk(KERN_INFO "%s: attempt to write to status register\n", + LIRC_DRIVER_NAME); + break; + } +} + +static unsigned int lirc_get_timer(void) +{ + return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT; +} + +static unsigned int lirc_get_signal(void) +{ + return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT; +} + +static void lirc_on(void) +{ + out(LIRC_PORT_DATA, tx_mask); +} + +static void lirc_off(void) +{ + out(LIRC_PORT_DATA, 0); +} + +static unsigned int init_lirc_timer(void) +{ + struct timeval tv, now; + unsigned int level, newlevel, timeelapsed, newtimer; + int count = 0; + + do_gettimeofday(&tv); + tv.tv_sec++; /* wait max. 1 sec. */ + level = lirc_get_timer(); + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + count++; + level = newlevel; + do_gettimeofday(&now); + } while (count < 1000 && (now.tv_sec < tv.tv_sec + || (now.tv_sec == tv.tv_sec + && now.tv_usec < tv.tv_usec))); + + timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000 + + (now.tv_usec - tv.tv_usec)); + if (count >= 1000 && timeelapsed > 0) { + if (default_timer == 0) { + /* autodetect timer */ + newtimer = (1000000*count)/timeelapsed; + printk(KERN_INFO "%s: %u Hz timer detected\n", + LIRC_DRIVER_NAME, newtimer); + return newtimer; + } else { + newtimer = (1000000*count)/timeelapsed; + if (abs(newtimer - default_timer) > default_timer/10) { + /* bad timer */ + printk(KERN_NOTICE "%s: bad timer: %u Hz\n", + LIRC_DRIVER_NAME, newtimer); + printk(KERN_NOTICE "%s: using default timer: " + "%u Hz\n", + LIRC_DRIVER_NAME, default_timer); + return default_timer; + } else { + printk(KERN_INFO "%s: %u Hz timer detected\n", + LIRC_DRIVER_NAME, newtimer); + return newtimer; /* use detected value */ + } + } + } else { + printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME); + return 0; + } +} + +static int lirc_claim(void) +{ + if (parport_claim(ppdevice) != 0) { + printk(KERN_WARNING "%s: could not claim port\n", + LIRC_DRIVER_NAME); + printk(KERN_WARNING "%s: waiting for port becoming available" + "\n", LIRC_DRIVER_NAME); + if (parport_claim_or_block(ppdevice) < 0) { + printk(KERN_NOTICE "%s: could not claim port, giving" + " up\n", LIRC_DRIVER_NAME); + return 0; + } + } + out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); + is_claimed = 1; + return 1; +} + +/*** interrupt handler ***/ + +static void rbuf_write(int signal) +{ + unsigned int nwptr; + + nwptr = (wptr + 1) & (RBUF_SIZE - 1); + if (nwptr == rptr) { + /* no new signals will be accepted */ + lost_irqs++; + printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME); + return; + } + rbuf[wptr] = signal; + wptr = nwptr; +} + +static void irq_handler(void *blah) +{ + struct timeval tv; + static struct timeval lasttv; + static int init; + long signal; + int data; + unsigned int level, newlevel; + unsigned int timeout; + + if (!is_open) + return; + + if (!is_claimed) + return; + +#if 0 + /* disable interrupt */ + disable_irq(irq); + out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN)); +#endif + if (check_pselecd && (in(1) & LP_PSELECD)) + return; + +#ifdef LIRC_TIMER + if (init) { + do_gettimeofday(&tv); + + signal = tv.tv_sec - lasttv.tv_sec; + if (signal > 15) + /* really long time */ + data = PULSE_MASK; + else + data = (int) (signal*1000000 + + tv.tv_usec - lasttv.tv_usec + + LIRC_SFH506_DELAY); + + rbuf_write(data); /* space */ + } else { + if (timer == 0) { + /* + * wake up; we'll lose this signal, but it will be + * garbage if the device is turned on anyway + */ + timer = init_lirc_timer(); + /* enable_irq(irq); */ + return; + } + init = 1; + } + + timeout = timer/10; /* timeout after 1/10 sec. */ + signal = 1; + level = lirc_get_timer(); + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + signal++; + level = newlevel; + + /* giving up */ + if (signal > timeout + || (check_pselecd && (in(1) & LP_PSELECD))) { + signal = 0; + printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME); + break; + } + } while (lirc_get_signal()); + + if (signal != 0) { + /* adjust value to usecs */ + __u64 helper; + + helper = ((__u64) signal)*1000000; + do_div(helper, timer); + signal = (long) helper; + + if (signal > LIRC_SFH506_DELAY) + data = signal - LIRC_SFH506_DELAY; + else + data = 1; + rbuf_write(PULSE_BIT|data); /* pulse */ + } + do_gettimeofday(&lasttv); +#else + /* add your code here */ +#endif + + wake_up_interruptible(&lirc_wait); + + /* enable interrupt */ + /* + enable_irq(irq); + out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN); + */ +} + +/*** file operations ***/ + +static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig) +{ + return -ESPIPE; +} + +static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos) +{ + int result = 0; + int count = 0; + DECLARE_WAITQUEUE(wait, current); + + if (n % sizeof(int)) + return -EINVAL; + + add_wait_queue(&lirc_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + while (count < n) { + if (rptr != wptr) { + if (copy_to_user(buf+count, (char *) &rbuf[rptr], + sizeof(int))) { + result = -EFAULT; + break; + } + rptr = (rptr + 1) & (RBUF_SIZE - 1); + count += sizeof(int); + } else { + if (filep->f_flags & O_NONBLOCK) { + result = -EAGAIN; + break; + } + if (signal_pending(current)) { + result = -ERESTARTSYS; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + } + remove_wait_queue(&lirc_wait, &wait); + set_current_state(TASK_RUNNING); + return count ? count : result; +} + +static ssize_t lirc_write(struct file *filep, const char *buf, size_t n, + loff_t *ppos) +{ + int count; + unsigned int i; + unsigned int level, newlevel; + unsigned long flags; + int counttimer; + int *wbuf; + ssize_t ret; + + if (!is_claimed) + return -EBUSY; + + count = n / sizeof(int); + + if (n % sizeof(int) || count % 2 == 0) + return -EINVAL; + + wbuf = memdup_user(buf, n); + if (IS_ERR(wbuf)) + return PTR_ERR(wbuf); + +#ifdef LIRC_TIMER + if (timer == 0) { + /* try again if device is ready */ + timer = init_lirc_timer(); + if (timer == 0) { + ret = -EIO; + goto out; + } + } + + /* adjust values from usecs */ + for (i = 0; i < count; i++) { + __u64 helper; + + helper = ((__u64) wbuf[i])*timer; + do_div(helper, 1000000); + wbuf[i] = (int) helper; + } + + local_irq_save(flags); + i = 0; + while (i < count) { + level = lirc_get_timer(); + counttimer = 0; + lirc_on(); + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + counttimer++; + level = newlevel; + if (check_pselecd && (in(1) & LP_PSELECD)) { + lirc_off(); + local_irq_restore(flags); + ret = -EIO; + goto out; + } + } while (counttimer < wbuf[i]); + i++; + + lirc_off(); + if (i == count) + break; + counttimer = 0; + do { + newlevel = lirc_get_timer(); + if (level == 0 && newlevel != 0) + counttimer++; + level = newlevel; + if (check_pselecd && (in(1) & LP_PSELECD)) { + local_irq_restore(flags); + ret = -EIO; + goto out; + } + } while (counttimer < wbuf[i]); + i++; + } + local_irq_restore(flags); +#else + /* place code that handles write without external timer here */ +#endif + ret = n; +out: + kfree(wbuf); + + return ret; +} + +static unsigned int lirc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &lirc_wait, wait); + if (rptr != wptr) + return POLLIN | POLLRDNORM; + return 0; +} + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int result; + __u32 features = LIRC_CAN_SET_TRANSMITTER_MASK | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; + __u32 mode; + __u32 value; + + switch (cmd) { + case LIRC_GET_FEATURES: + result = put_user(features, (__u32 *) arg); + if (result) + return result; + break; + case LIRC_GET_SEND_MODE: + result = put_user(LIRC_MODE_PULSE, (__u32 *) arg); + if (result) + return result; + break; + case LIRC_GET_REC_MODE: + result = put_user(LIRC_MODE_MODE2, (__u32 *) arg); + if (result) + return result; + break; + case LIRC_SET_SEND_MODE: + result = get_user(mode, (__u32 *) arg); + if (result) + return result; + if (mode != LIRC_MODE_PULSE) + return -EINVAL; + break; + case LIRC_SET_REC_MODE: + result = get_user(mode, (__u32 *) arg); + if (result) + return result; + if (mode != LIRC_MODE_MODE2) + return -ENOSYS; + break; + case LIRC_SET_TRANSMITTER_MASK: + result = get_user(value, (__u32 *) arg); + if (result) + return result; + if ((value & LIRC_PARALLEL_TRANSMITTER_MASK) != value) + return LIRC_PARALLEL_MAX_TRANSMITTERS; + tx_mask = value; + break; + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static int lirc_open(struct inode *node, struct file *filep) +{ + if (is_open || !lirc_claim()) + return -EBUSY; + + parport_enable_irq(pport); + + /* init read ptr */ + rptr = 0; + wptr = 0; + lost_irqs = 0; + + is_open = 1; + return 0; +} + +static int lirc_close(struct inode *node, struct file *filep) +{ + if (is_claimed) { + is_claimed = 0; + parport_release(ppdevice); + } + is_open = 0; + return 0; +} + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .llseek = lirc_lseek, + .read = lirc_read, + .write = lirc_write, + .poll = lirc_poll, + .unlocked_ioctl = lirc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lirc_ioctl, +#endif + .open = lirc_open, + .release = lirc_close +}; + +static int set_use_inc(void *data) +{ + return 0; +} + +static void set_use_dec(void *data) +{ +} + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + +static struct platform_device *lirc_parallel_dev; + +static int __devinit lirc_parallel_probe(struct platform_device *dev) +{ + return 0; +} + +static int __devexit lirc_parallel_remove(struct platform_device *dev) +{ + return 0; +} + +static int lirc_parallel_suspend(struct platform_device *dev, + pm_message_t state) +{ + return 0; +} + +static int lirc_parallel_resume(struct platform_device *dev) +{ + return 0; +} + +static struct platform_driver lirc_parallel_driver = { + .probe = lirc_parallel_probe, + .remove = __devexit_p(lirc_parallel_remove), + .suspend = lirc_parallel_suspend, + .resume = lirc_parallel_resume, + .driver = { + .name = LIRC_DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int pf(void *handle) +{ + parport_disable_irq(pport); + is_claimed = 0; + return 0; +} + +static void kf(void *handle) +{ + if (!is_open) + return; + if (!lirc_claim()) + return; + parport_enable_irq(pport); + lirc_off(); + /* this is a bit annoying when you actually print...*/ + /* + printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME); + */ +} + +/*** module initialization and cleanup ***/ + +static int __init lirc_parallel_init(void) +{ + int result; + + result = platform_driver_register(&lirc_parallel_driver); + if (result) { + printk(KERN_NOTICE "platform_driver_register" + " returned %d\n", result); + return result; + } + + lirc_parallel_dev = platform_device_alloc(LIRC_DRIVER_NAME, 0); + if (!lirc_parallel_dev) { + result = -ENOMEM; + goto exit_driver_unregister; + } + + result = platform_device_add(lirc_parallel_dev); + if (result) + goto exit_device_put; + + pport = parport_find_base(io); + if (pport == NULL) { + printk(KERN_NOTICE "%s: no port at %x found\n", + LIRC_DRIVER_NAME, io); + result = -ENXIO; + goto exit_device_put; + } + ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME, + pf, kf, irq_handler, 0, NULL); + parport_put_port(pport); + if (ppdevice == NULL) { + printk(KERN_NOTICE "%s: parport_register_device() failed\n", + LIRC_DRIVER_NAME); + result = -ENXIO; + goto exit_device_put; + } + if (parport_claim(ppdevice) != 0) + goto skip_init; + is_claimed = 1; + out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP); + +#ifdef LIRC_TIMER + if (debug) + out(LIRC_PORT_DATA, tx_mask); + + timer = init_lirc_timer(); + +#if 0 /* continue even if device is offline */ + if (timer == 0) { + is_claimed = 0; + parport_release(pport); + parport_unregister_device(ppdevice); + result = -EIO; + goto exit_device_put; + } + +#endif + if (debug) + out(LIRC_PORT_DATA, 0); +#endif + + is_claimed = 0; + parport_release(ppdevice); + skip_init: + driver.dev = &lirc_parallel_dev->dev; + driver.minor = lirc_register_driver(&driver); + if (driver.minor < 0) { + printk(KERN_NOTICE "%s: register_chrdev() failed\n", + LIRC_DRIVER_NAME); + parport_unregister_device(ppdevice); + result = -EIO; + goto exit_device_put; + } + printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n", + LIRC_DRIVER_NAME, io, irq); + return 0; + +exit_device_put: + platform_device_put(lirc_parallel_dev); +exit_driver_unregister: + platform_driver_unregister(&lirc_parallel_driver); + return result; +} + +static void __exit lirc_parallel_exit(void) +{ + parport_unregister_device(ppdevice); + lirc_unregister_driver(driver.minor); + + platform_device_unregister(lirc_parallel_dev); + platform_driver_unregister(&lirc_parallel_driver); +} + +module_init(lirc_parallel_init); +module_exit(lirc_parallel_exit); + +MODULE_DESCRIPTION("Infrared receiver driver for parallel ports."); +MODULE_AUTHOR("Christoph Bartelmus"); +MODULE_LICENSE("GPL"); + +module_param(io, int, S_IRUGO); +MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)"); + +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (7 or 5)"); + +module_param(tx_mask, int, S_IRUGO); +MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); + +module_param(check_pselecd, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Check for printer (default: 0)"); diff --git a/drivers/staging/media/lirc/lirc_parallel.h b/drivers/staging/media/lirc/lirc_parallel.h new file mode 100644 index 000000000000..4bed6afe0632 --- /dev/null +++ b/drivers/staging/media/lirc/lirc_parallel.h @@ -0,0 +1,26 @@ +/* lirc_parallel.h */ + +#ifndef _LIRC_PARALLEL_H +#define _LIRC_PARALLEL_H + +#include + +#define LIRC_PORT_LEN 3 + +#define LIRC_LP_BASE 0 +#define LIRC_LP_STATUS 1 +#define LIRC_LP_CONTROL 2 + +#define LIRC_PORT_DATA LIRC_LP_BASE /* base */ +#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */ +#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */ +#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */ +#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */ +#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */ + +#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */ + +#define LIRC_PARALLEL_MAX_TRANSMITTERS 8 +#define LIRC_PARALLEL_TRANSMITTER_MASK ((1< + * Tim Davies + * + * This driver was derived from: + * Venky Raju + * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD" + * Paul Miller 's 2003-2004 + * "lirc_atiusb - USB remote support for LIRC" + * Culver Consulting Services 's 2003 + * "Sasem OnAir VFD/IR USB driver" + * + * + * NOTE - The LCDproc iMon driver should work with this module. More info at + * http://www.frogstorm.info/sasem + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + + +#define MOD_AUTHOR "Oliver Stabel , " \ + "Tim Davies " +#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1" +#define MOD_NAME "lirc_sasem" +#define MOD_VERSION "0.5" + +#define VFD_MINOR_BASE 144 /* Same as LCD */ +#define DEVICE_NAME "lcd%d" + +#define BUF_CHUNK_SIZE 8 +#define BUF_SIZE 128 + +#define IOCTL_LCD_CONTRAST 1 + +/*** P R O T O T Y P E S ***/ + +/* USB Callback prototypes */ +static int sasem_probe(struct usb_interface *interface, + const struct usb_device_id *id); +static void sasem_disconnect(struct usb_interface *interface); +static void usb_rx_callback(struct urb *urb); +static void usb_tx_callback(struct urb *urb); + +/* VFD file_operations function prototypes */ +static int vfd_open(struct inode *inode, struct file *file); +static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg); +static int vfd_close(struct inode *inode, struct file *file); +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos); + +/* LIRC driver function prototypes */ +static int ir_open(void *data); +static void ir_close(void *data); + +/* Driver init/exit prototypes */ +static int __init sasem_init(void); +static void __exit sasem_exit(void); + +/*** G L O B A L S ***/ +#define SASEM_DATA_BUF_SZ 32 + +struct sasem_context { + + struct usb_device *dev; + int vfd_isopen; /* VFD port has been opened */ + unsigned int vfd_contrast; /* VFD contrast */ + int ir_isopen; /* IR port has been opened */ + int dev_present; /* USB device presence */ + struct mutex ctx_lock; /* to lock this object */ + wait_queue_head_t remove_ok; /* For unexpected USB disconnects */ + + struct lirc_driver *driver; + struct usb_endpoint_descriptor *rx_endpoint; + struct usb_endpoint_descriptor *tx_endpoint; + struct urb *rx_urb; + struct urb *tx_urb; + unsigned char usb_rx_buf[8]; + unsigned char usb_tx_buf[8]; + + struct tx_t { + unsigned char data_buf[SASEM_DATA_BUF_SZ]; /* user data buffer */ + struct completion finished; /* wait for write to finish */ + atomic_t busy; /* write in progress */ + int status; /* status of tx completion */ + } tx; + + /* for dealing with repeat codes (wish there was a toggle bit!) */ + struct timeval presstime; + char lastcode[8]; + int codesaved; +}; + +/* VFD file operations */ +static const struct file_operations vfd_fops = { + .owner = THIS_MODULE, + .open = &vfd_open, + .write = &vfd_write, + .unlocked_ioctl = &vfd_ioctl, + .release = &vfd_close, + .llseek = noop_llseek, +}; + +/* USB Device ID for Sasem USB Control Board */ +static struct usb_device_id sasem_usb_id_table[] = { + /* Sasem USB Control Board */ + { USB_DEVICE(0x11ba, 0x0101) }, + /* Terminating entry */ + {} +}; + +/* USB Device data */ +static struct usb_driver sasem_driver = { + .name = MOD_NAME, + .probe = sasem_probe, + .disconnect = sasem_disconnect, + .id_table = sasem_usb_id_table, +}; + +static struct usb_class_driver sasem_class = { + .name = DEVICE_NAME, + .fops = &vfd_fops, + .minor_base = VFD_MINOR_BASE, +}; + +/* to prevent races between open() and disconnect() */ +static DEFINE_MUTEX(disconnect_lock); + +static int debug; + + +/*** M O D U L E C O D E ***/ + +MODULE_AUTHOR(MOD_AUTHOR); +MODULE_DESCRIPTION(MOD_DESC); +MODULE_LICENSE("GPL"); +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)"); + +static void delete_context(struct sasem_context *context) +{ + usb_free_urb(context->tx_urb); /* VFD */ + usb_free_urb(context->rx_urb); /* IR */ + lirc_buffer_free(context->driver->rbuf); + kfree(context->driver->rbuf); + kfree(context->driver); + kfree(context); + + if (debug) + printk(KERN_INFO "%s: context deleted\n", __func__); +} + +static void deregister_from_lirc(struct sasem_context *context) +{ + int retval; + int minor = context->driver->minor; + + retval = lirc_unregister_driver(minor); + if (retval) + err("%s: unable to deregister from lirc (%d)", + __func__, retval); + else + printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n", + minor); + +} + +/** + * Called when the VFD device (e.g. /dev/usb/lcd) + * is opened by the application. + */ +static int vfd_open(struct inode *inode, struct file *file) +{ + struct usb_interface *interface; + struct sasem_context *context = NULL; + int subminor; + int retval = 0; + + /* prevent races with disconnect */ + mutex_lock(&disconnect_lock); + + subminor = iminor(inode); + interface = usb_find_interface(&sasem_driver, subminor); + if (!interface) { + err("%s: could not find interface for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + context = usb_get_intfdata(interface); + + if (!context) { + err("%s: no context found for minor %d", + __func__, subminor); + retval = -ENODEV; + goto exit; + } + + mutex_lock(&context->ctx_lock); + + if (context->vfd_isopen) { + err("%s: VFD port is already open", __func__); + retval = -EBUSY; + } else { + context->vfd_isopen = 1; + file->private_data = context; + printk(KERN_INFO "VFD port opened\n"); + } + + mutex_unlock(&context->ctx_lock); + +exit: + mutex_unlock(&disconnect_lock); + return retval; +} + +/** + * Called when the VFD device (e.g. /dev/usb/lcd) + * is closed by the application. + */ +static long vfd_ioctl(struct file *file, unsigned cmd, unsigned long arg) +{ + struct sasem_context *context = NULL; + + context = (struct sasem_context *) file->private_data; + + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + switch (cmd) { + case IOCTL_LCD_CONTRAST: + if (arg > 1000) + arg = 1000; + context->vfd_contrast = (unsigned int)arg; + break; + default: + printk(KERN_INFO "Unknown IOCTL command\n"); + mutex_unlock(&context->ctx_lock); + return -ENOIOCTLCMD; /* not supported */ + } + + mutex_unlock(&context->ctx_lock); + return 0; +} + +/** + * Called when the VFD device (e.g. /dev/usb/lcd) + * is closed by the application. + */ +static int vfd_close(struct inode *inode, struct file *file) +{ + struct sasem_context *context = NULL; + int retval = 0; + + context = (struct sasem_context *) file->private_data; + + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->vfd_isopen) { + err("%s: VFD is not open", __func__); + retval = -EIO; + } else { + context->vfd_isopen = 0; + printk(KERN_INFO "VFD port closed\n"); + if (!context->dev_present && !context->ir_isopen) { + + /* Device disconnected before close and IR port is + * not open. If IR port is open, context will be + * deleted by ir_close. */ + mutex_unlock(&context->ctx_lock); + delete_context(context); + return retval; + } + } + + mutex_unlock(&context->ctx_lock); + return retval; +} + +/** + * Sends a packet to the VFD. + */ +static int send_packet(struct sasem_context *context) +{ + unsigned int pipe; + int interval = 0; + int retval = 0; + + pipe = usb_sndintpipe(context->dev, + context->tx_endpoint->bEndpointAddress); + interval = context->tx_endpoint->bInterval; + + usb_fill_int_urb(context->tx_urb, context->dev, pipe, + context->usb_tx_buf, sizeof(context->usb_tx_buf), + usb_tx_callback, context, interval); + + context->tx_urb->actual_length = 0; + + init_completion(&context->tx.finished); + atomic_set(&(context->tx.busy), 1); + + retval = usb_submit_urb(context->tx_urb, GFP_KERNEL); + if (retval) { + atomic_set(&(context->tx.busy), 0); + err("%s: error submitting urb (%d)", __func__, retval); + } else { + /* Wait for transmission to complete (or abort) */ + mutex_unlock(&context->ctx_lock); + wait_for_completion(&context->tx.finished); + mutex_lock(&context->ctx_lock); + + retval = context->tx.status; + if (retval) + err("%s: packet tx failed (%d)", __func__, retval); + } + + return retval; +} + +/** + * Writes data to the VFD. The Sasem VFD is 2x16 characters + * and requires data in 9 consecutive USB interrupt packets, + * each packet carrying 8 bytes. + */ +static ssize_t vfd_write(struct file *file, const char *buf, + size_t n_bytes, loff_t *pos) +{ + int i; + int retval = 0; + struct sasem_context *context; + int *data_buf = NULL; + + context = (struct sasem_context *) file->private_data; + if (!context) { + err("%s: no context for device", __func__); + return -ENODEV; + } + + mutex_lock(&context->ctx_lock); + + if (!context->dev_present) { + err("%s: no Sasem device present", __func__); + retval = -ENODEV; + goto exit; + } + + if (n_bytes <= 0 || n_bytes > SASEM_DATA_BUF_SZ) { + err("%s: invalid payload size", __func__); + retval = -EINVAL; + goto exit; + } + + data_buf = memdup_user(buf, n_bytes); + if (IS_ERR(data_buf)) { + retval = PTR_ERR(data_buf); + goto exit; + } + + memcpy(context->tx.data_buf, data_buf, n_bytes); + + /* Pad with spaces */ + for (i = n_bytes; i < SASEM_DATA_BUF_SZ; ++i) + context->tx.data_buf[i] = ' '; + + /* Nine 8 byte packets to be sent */ + /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0" + * will clear the VFD */ + for (i = 0; i < 9; i++) { + switch (i) { + case 0: + memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8); + context->usb_tx_buf[1] = (context->vfd_contrast) ? + (0x2B - (context->vfd_contrast - 1) / 250) + : 0x2B; + break; + case 1: + memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); + break; + case 2: + memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8); + break; + case 3: + memcpy(context->usb_tx_buf, context->tx.data_buf, 8); + break; + case 4: + memcpy(context->usb_tx_buf, + context->tx.data_buf + 8, 8); + break; + case 5: + memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8); + break; + case 6: + memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8); + break; + case 7: + memcpy(context->usb_tx_buf, + context->tx.data_buf + 16, 8); + break; + case 8: + memcpy(context->usb_tx_buf, + context->tx.data_buf + 24, 8); + break; + } + retval = send_packet(context); + if (retval) { + + err("%s: send packet failed for packet #%d", + __func__, i); + goto exit; + } + } +exit: + + mutex_unlock(&context->ctx_lock); + kfree(data_buf); + + return (!retval) ? n_bytes : retval; +} + +/** + * Callback function for USB core API: transmit data + */ +static void usb_tx_callback(struct urb *urb) +{ + struct sasem_context *context; + + if (!urb) + return; + context = (struct sasem_context *) urb->context; + if (!context) + return; + + context->tx.status = urb->status; + + /* notify waiters that write has finished */ + atomic_set(&context->tx.busy, 0); + complete(&context->tx.finished); + + return; +} + +/** + * Called by lirc_dev when the application opens /dev/lirc + */ +static int ir_open(void *data) +{ + int retval = 0; + struct sasem_context *context; + + /* prevent races with disconnect */ + mutex_lock(&disconnect_lock); + + context = (struct sasem_context *) data; + + mutex_lock(&context->ctx_lock); + + if (context->ir_isopen) { + err("%s: IR port is already open", __func__); + retval = -EBUSY; + goto exit; + } + + usb_fill_int_urb(context->rx_urb, context->dev, + usb_rcvintpipe(context->dev, + context->rx_endpoint->bEndpointAddress), + context->usb_rx_buf, sizeof(context->usb_rx_buf), + usb_rx_callback, context, context->rx_endpoint->bInterval); + + retval = usb_submit_urb(context->rx_urb, GFP_KERNEL); + + if (retval) + err("%s: usb_submit_urb failed for ir_open (%d)", + __func__, retval); + else { + context->ir_isopen = 1; + printk(KERN_INFO "IR port opened\n"); + } + +exit: + mutex_unlock(&context->ctx_lock); + + mutex_unlock(&disconnect_lock); + return retval; +} + +/** + * Called by lirc_dev when the application closes /dev/lirc + */ +static void ir_close(void *data) +{ + struct sasem_context *context; + + context = (struct sasem_context *)data; + if (!context) { + err("%s: no context for device", __func__); + return; + } + + mutex_lock(&context->ctx_lock); + + usb_kill_urb(context->rx_urb); + context->ir_isopen = 0; + printk(KERN_INFO "IR port closed\n"); + + if (!context->dev_present) { + + /* + * Device disconnected while IR port was + * still open. Driver was not deregistered + * at disconnect time, so do it now. + */ + deregister_from_lirc(context); + + if (!context->vfd_isopen) { + + mutex_unlock(&context->ctx_lock); + delete_context(context); + return; + } + /* If VFD port is open, context will be deleted by vfd_close */ + } + + mutex_unlock(&context->ctx_lock); + return; +} + +/** + * Process the incoming packet + */ +static void incoming_packet(struct sasem_context *context, + struct urb *urb) +{ + int len = urb->actual_length; + unsigned char *buf = urb->transfer_buffer; + long ms; + struct timeval tv; + int i; + + if (len != 8) { + printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n", + __func__, len); + return; + } + + if (debug) { + printk(KERN_INFO "Incoming data: "); + for (i = 0; i < 8; ++i) + printk(KERN_CONT "%02x ", buf[i]); + printk(KERN_CONT "\n"); + } + + /* + * Lirc could deal with the repeat code, but we really need to block it + * if it arrives too late. Otherwise we could repeat the wrong code. + */ + + /* get the time since the last button press */ + do_gettimeofday(&tv); + ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 + + (tv.tv_usec - context->presstime.tv_usec) / 1000; + + if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) { + /* + * the repeat code is being sent, so we copy + * the old code to LIRC + */ + + /* + * NOTE: Only if the last code was less than 250ms ago + * - no one should be able to push another (undetected) button + * in that time and then get a false repeat of the previous + * press but it is long enough for a genuine repeat + */ + if ((ms < 250) && (context->codesaved != 0)) { + memcpy(buf, &context->lastcode, 8); + context->presstime.tv_sec = tv.tv_sec; + context->presstime.tv_usec = tv.tv_usec; + } + } else { + /* save the current valid code for repeats */ + memcpy(&context->lastcode, buf, 8); + /* + * set flag to signal a valid code was save; + * just for safety reasons + */ + context->codesaved = 1; + context->presstime.tv_sec = tv.tv_sec; + context->presstime.tv_usec = tv.tv_usec; + } + + lirc_buffer_write(context->driver->rbuf, buf); + wake_up(&context->driver->rbuf->wait_poll); +} + +/** + * Callback function for USB core API: receive data + */ +static void usb_rx_callback(struct urb *urb) +{ + struct sasem_context *context; + + if (!urb) + return; + context = (struct sasem_context *) urb->context; + if (!context) + return; + + switch (urb->status) { + + case -ENOENT: /* usbcore unlink successful! */ + return; + + case 0: + if (context->ir_isopen) + incoming_packet(context, urb); + break; + + default: + printk(KERN_WARNING "%s: status (%d): ignored", + __func__, urb->status); + break; + } + + usb_submit_urb(context->rx_urb, GFP_ATOMIC); + return; +} + + + +/** + * Callback function for USB core API: Probe + */ +static int sasem_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *dev = NULL; + struct usb_host_interface *iface_desc = NULL; + struct usb_endpoint_descriptor *rx_endpoint = NULL; + struct usb_endpoint_descriptor *tx_endpoint = NULL; + struct urb *rx_urb = NULL; + struct urb *tx_urb = NULL; + struct lirc_driver *driver = NULL; + struct lirc_buffer *rbuf = NULL; + int lirc_minor = 0; + int num_endpoints; + int retval = 0; + int vfd_ep_found; + int ir_ep_found; + int alloc_status; + struct sasem_context *context = NULL; + int i; + + printk(KERN_INFO "%s: found Sasem device\n", __func__); + + + dev = usb_get_dev(interface_to_usbdev(interface)); + iface_desc = interface->cur_altsetting; + num_endpoints = iface_desc->desc.bNumEndpoints; + + /* + * Scan the endpoint list and set: + * first input endpoint = IR endpoint + * first output endpoint = VFD endpoint + */ + + ir_ep_found = 0; + vfd_ep_found = 0; + + for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) { + + struct usb_endpoint_descriptor *ep; + int ep_dir; + int ep_type; + ep = &iface_desc->endpoint [i].desc; + ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK; + ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + if (!ir_ep_found && + ep_dir == USB_DIR_IN && + ep_type == USB_ENDPOINT_XFER_INT) { + + rx_endpoint = ep; + ir_ep_found = 1; + if (debug) + printk(KERN_INFO "%s: found IR endpoint\n", + __func__); + + } else if (!vfd_ep_found && + ep_dir == USB_DIR_OUT && + ep_type == USB_ENDPOINT_XFER_INT) { + + tx_endpoint = ep; + vfd_ep_found = 1; + if (debug) + printk(KERN_INFO "%s: found VFD endpoint\n", + __func__); + } + } + + /* Input endpoint is mandatory */ + if (!ir_ep_found) { + + err("%s: no valid input (IR) endpoint found.", __func__); + retval = -ENODEV; + goto exit; + } + + if (!vfd_ep_found) + printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n", + __func__); + + + /* Allocate memory */ + alloc_status = 0; + + context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL); + if (!context) { + err("%s: kzalloc failed for context", __func__); + alloc_status = 1; + goto alloc_status_switch; + } + driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); + if (!driver) { + err("%s: kzalloc failed for lirc_driver", __func__); + alloc_status = 2; + goto alloc_status_switch; + } + rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); + if (!rbuf) { + err("%s: kmalloc failed for lirc_buffer", __func__); + alloc_status = 3; + goto alloc_status_switch; + } + if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) { + err("%s: lirc_buffer_init failed", __func__); + alloc_status = 4; + goto alloc_status_switch; + } + rx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!rx_urb) { + err("%s: usb_alloc_urb failed for IR urb", __func__); + alloc_status = 5; + goto alloc_status_switch; + } + if (vfd_ep_found) { + tx_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!tx_urb) { + err("%s: usb_alloc_urb failed for VFD urb", + __func__); + alloc_status = 6; + goto alloc_status_switch; + } + } + + mutex_init(&context->ctx_lock); + + strcpy(driver->name, MOD_NAME); + driver->minor = -1; + driver->code_length = 64; + driver->sample_rate = 0; + driver->features = LIRC_CAN_REC_LIRCCODE; + driver->data = context; + driver->rbuf = rbuf; + driver->set_use_inc = ir_open; + driver->set_use_dec = ir_close; + driver->dev = &interface->dev; + driver->owner = THIS_MODULE; + + mutex_lock(&context->ctx_lock); + + lirc_minor = lirc_register_driver(driver); + if (lirc_minor < 0) { + err("%s: lirc_register_driver failed", __func__); + alloc_status = 7; + retval = lirc_minor; + goto unlock; + } else + printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n", + __func__, lirc_minor); + + /* Needed while unregistering! */ + driver->minor = lirc_minor; + + context->dev = dev; + context->dev_present = 1; + context->rx_endpoint = rx_endpoint; + context->rx_urb = rx_urb; + if (vfd_ep_found) { + context->tx_endpoint = tx_endpoint; + context->tx_urb = tx_urb; + context->vfd_contrast = 1000; /* range 0 - 1000 */ + } + context->driver = driver; + + usb_set_intfdata(interface, context); + + if (vfd_ep_found) { + + if (debug) + printk(KERN_INFO "Registering VFD with sysfs\n"); + if (usb_register_dev(interface, &sasem_class)) + /* Not a fatal error, so ignore */ + printk(KERN_INFO "%s: could not get a minor number " + "for VFD\n", __func__); + } + + printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n", + __func__, dev->bus->busnum, dev->devnum); +unlock: + mutex_unlock(&context->ctx_lock); + +alloc_status_switch: + switch (alloc_status) { + + case 7: + if (vfd_ep_found) + usb_free_urb(tx_urb); + case 6: + usb_free_urb(rx_urb); + case 5: + lirc_buffer_free(rbuf); + case 4: + kfree(rbuf); + case 3: + kfree(driver); + case 2: + kfree(context); + context = NULL; + case 1: + if (retval == 0) + retval = -ENOMEM; + } + +exit: + return retval; +} + +/** + * Callback function for USB core API: disonnect + */ +static void sasem_disconnect(struct usb_interface *interface) +{ + struct sasem_context *context; + + /* prevent races with ir_open()/vfd_open() */ + mutex_lock(&disconnect_lock); + + context = usb_get_intfdata(interface); + mutex_lock(&context->ctx_lock); + + printk(KERN_INFO "%s: Sasem device disconnected\n", __func__); + + usb_set_intfdata(interface, NULL); + context->dev_present = 0; + + /* Stop reception */ + usb_kill_urb(context->rx_urb); + + /* Abort ongoing write */ + if (atomic_read(&context->tx.busy)) { + + usb_kill_urb(context->tx_urb); + wait_for_completion(&context->tx.finished); + } + + /* De-register from lirc_dev if IR port is not open */ + if (!context->ir_isopen) + deregister_from_lirc(context); + + usb_deregister_dev(interface, &sasem_class); + + mutex_unlock(&context->ctx_lock); + + if (!context->ir_isopen && !context->vfd_isopen) + delete_context(context); + + mutex_unlock(&disconnect_lock); +} + +static int __init sasem_init(void) +{ + int rc; + + printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n"); + printk(KERN_INFO MOD_AUTHOR "\n"); + + rc = usb_register(&sasem_driver); + if (rc < 0) { + err("%s: usb register failed (%d)", __func__, rc); + return -ENODEV; + } + return 0; +} + +static void __exit sasem_exit(void) +{ + usb_deregister(&sasem_driver); + printk(KERN_INFO "module removed. Goodbye!\n"); +} + + +module_init(sasem_init); +module_exit(sasem_exit); diff --git a/drivers/staging/media/lirc/lirc_serial.c b/drivers/staging/media/lirc/lirc_serial.c new file mode 100644 index 000000000000..8a060a8a7224 --- /dev/null +++ b/drivers/staging/media/lirc/lirc_serial.c @@ -0,0 +1,1315 @@ +/* + * lirc_serial.c + * + * lirc_serial - Device driver that records pulse- and pause-lengths + * (space-lengths) between DDCD event on a serial port. + * + * Copyright (C) 1996,97 Ralph Metzler + * Copyright (C) 1998 Trent Piepho + * Copyright (C) 1998 Ben Pfaff + * Copyright (C) 1999 Christoph Bartelmus + * Copyright (C) 2007 Andrei Tanas (suspend/resume 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* + * Steve's changes to improve transmission fidelity: + * - for systems with the rdtsc instruction and the clock counter, a + * send_pule that times the pulses directly using the counter. + * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is + * not needed. Measurement shows very stable waveform, even where + * PCI activity slows the access to the UART, which trips up other + * versions. + * - For other system, non-integer-microsecond pulse/space lengths, + * done using fixed point binary. So, much more accurate carrier + * frequency. + * - fine tuned transmitter latency, taking advantage of fractional + * microseconds in previous change + * - Fixed bug in the way transmitter latency was accounted for by + * tuning the pulse lengths down - the send_pulse routine ignored + * this overhead as it timed the overall pulse length - so the + * pulse frequency was right but overall pulse length was too + * long. Fixed by accounting for latency on each pulse/space + * iteration. + * + * Steve Davies July 2001 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef CONFIG_LIRC_SERIAL_NSLU2 +#include +#endif +/* From Intel IXP42X Developer's Manual (#252480-005): */ +/* ftp://download.intel.com/design/network/manuals/25248005.pdf */ +#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */ +#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */ + +#include +#include + +#define LIRC_DRIVER_NAME "lirc_serial" + +struct lirc_serial { + int signal_pin; + int signal_pin_change; + u8 on; + u8 off; + long (*send_pulse)(unsigned long length); + void (*send_space)(long length); + int features; + spinlock_t lock; +}; + +#define LIRC_HOMEBREW 0 +#define LIRC_IRDEO 1 +#define LIRC_IRDEO_REMOTE 2 +#define LIRC_ANIMAX 3 +#define LIRC_IGOR 4 +#define LIRC_NSLU2 5 + +/*** module parameters ***/ +static int type; +static int io; +static int irq; +static int iommap; +static int ioshift; +static int softcarrier = 1; +static int share_irq; +static int debug; +static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */ +static int txsense; /* 0 = active high, 1 = active low */ + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ + fmt, ## args); \ + } while (0) + +/* forward declarations */ +static long send_pulse_irdeo(unsigned long length); +static long send_pulse_homebrew(unsigned long length); +static void send_space_irdeo(long length); +static void send_space_homebrew(long length); + +static struct lirc_serial hardware[] = { + [LIRC_HOMEBREW] = { + .signal_pin = UART_MSR_DCD, + .signal_pin_change = UART_MSR_DDCD, + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), + .off = (UART_MCR_RTS | UART_MCR_OUT2), + .send_pulse = send_pulse_homebrew, + .send_space = send_space_homebrew, +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) +#else + .features = LIRC_CAN_REC_MODE2 +#endif + }, + + [LIRC_IRDEO] = { + .signal_pin = UART_MSR_DSR, + .signal_pin_change = UART_MSR_DDSR, + .on = UART_MCR_OUT2, + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .send_pulse = send_pulse_irdeo, + .send_space = send_space_irdeo, + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) + }, + + [LIRC_IRDEO_REMOTE] = { + .signal_pin = UART_MSR_DSR, + .signal_pin_change = UART_MSR_DDSR, + .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .send_pulse = send_pulse_irdeo, + .send_space = send_space_irdeo, + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) + }, + + [LIRC_ANIMAX] = { + .signal_pin = UART_MSR_DCD, + .signal_pin_change = UART_MSR_DDCD, + .on = 0, + .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2), + .send_pulse = NULL, + .send_space = NULL, + .features = LIRC_CAN_REC_MODE2 + }, + + [LIRC_IGOR] = { + .signal_pin = UART_MSR_DSR, + .signal_pin_change = UART_MSR_DDSR, + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), + .off = (UART_MCR_RTS | UART_MCR_OUT2), + .send_pulse = send_pulse_homebrew, + .send_space = send_space_homebrew, +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) +#else + .features = LIRC_CAN_REC_MODE2 +#endif + }, + +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + /* + * Modified Linksys Network Storage Link USB 2.0 (NSLU2): + * We receive on CTS of the 2nd serial port (R142,LHS), we + * transmit with a IR diode between GPIO[1] (green status LED), + * and ground (Matthias Goebl ). + * See also http://www.nslu2-linux.org for this device + */ + [LIRC_NSLU2] = { + .signal_pin = UART_MSR_CTS, + .signal_pin_change = UART_MSR_DCTS, + .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR), + .off = (UART_MCR_RTS | UART_MCR_OUT2), + .send_pulse = send_pulse_homebrew, + .send_space = send_space_homebrew, +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER + .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2) +#else + .features = LIRC_CAN_REC_MODE2 +#endif + }, +#endif + +}; + +#define RS_ISR_PASS_LIMIT 256 + +/* + * A long pulse code from a remote might take up to 300 bytes. The + * daemon should read the bytes as soon as they are generated, so take + * the number of keys you think you can push before the daemon runs + * and multiply by 300. The driver will warn you if you overrun this + * buffer. If you have a slow computer or non-busmastering IDE disks, + * maybe you will need to increase this. + */ + +/* This MUST be a power of two! It has to be larger than 1 as well. */ + +#define RBUF_LEN 256 + +static struct timeval lasttv = {0, 0}; + +static struct lirc_buffer rbuf; + +static unsigned int freq = 38000; +static unsigned int duty_cycle = 50; + +/* Initialized in init_timing_params() */ +static unsigned long period; +static unsigned long pulse_width; +static unsigned long space_width; + +#if defined(__i386__) +/* + * From: + * Linux I/O port programming mini-HOWTO + * Author: Riku Saikkonen + * v, 28 December 1997 + * + * [...] + * Actually, a port I/O instruction on most ports in the 0-0x3ff range + * takes almost exactly 1 microsecond, so if you're, for example, using + * the parallel port directly, just do additional inb()s from that port + * to delay. + * [...] + */ +/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from + * comment above plus trimming to match actual measured frequency. + * This will be sensitive to cpu speed, though hopefully most of the 1.5us + * is spent in the uart access. Still - for reference test machine was a + * 1.13GHz Athlon system - Steve + */ + +/* + * changed from 400 to 450 as this works better on slower machines; + * faster machines will use the rdtsc code anyway + */ +#define LIRC_SERIAL_TRANSMITTER_LATENCY 450 + +#else + +/* does anybody have information on other platforms ? */ +/* 256 = 1<<8 */ +#define LIRC_SERIAL_TRANSMITTER_LATENCY 256 + +#endif /* __i386__ */ +/* + * FIXME: should we be using hrtimers instead of this + * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense? + */ + +/* fetch serial input packet (1 byte) from register offset */ +static u8 sinp(int offset) +{ + if (iommap != 0) + /* the register is memory-mapped */ + offset <<= ioshift; + + return inb(io + offset); +} + +/* write serial output packet (1 byte) of value to register offset */ +static void soutp(int offset, u8 value) +{ + if (iommap != 0) + /* the register is memory-mapped */ + offset <<= ioshift; + + outb(value, io + offset); +} + +static void on(void) +{ +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + /* + * On NSLU2, we put the transmit diode between the output of the green + * status LED and ground + */ + if (type == LIRC_NSLU2) { + gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW); + return; + } +#endif + if (txsense) + soutp(UART_MCR, hardware[type].off); + else + soutp(UART_MCR, hardware[type].on); +} + +static void off(void) +{ +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + if (type == LIRC_NSLU2) { + gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH); + return; + } +#endif + if (txsense) + soutp(UART_MCR, hardware[type].on); + else + soutp(UART_MCR, hardware[type].off); +} + +#ifndef MAX_UDELAY_MS +#define MAX_UDELAY_US 5000 +#else +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) +#endif + +static void safe_udelay(unsigned long usecs) +{ + while (usecs > MAX_UDELAY_US) { + udelay(MAX_UDELAY_US); + usecs -= MAX_UDELAY_US; + } + udelay(usecs); +} + +#ifdef USE_RDTSC +/* + * This is an overflow/precision juggle, complicated in that we can't + * do long long divide in the kernel + */ + +/* + * When we use the rdtsc instruction to measure clocks, we keep the + * pulse and space widths as clock cycles. As this is CPU speed + * dependent, the widths must be calculated in init_port and ioctl + * time + */ + +/* So send_pulse can quickly convert microseconds to clocks */ +static unsigned long conv_us_to_clocks; + +static int init_timing_params(unsigned int new_duty_cycle, + unsigned int new_freq) +{ + __u64 loops_per_sec, work; + + duty_cycle = new_duty_cycle; + freq = new_freq; + + loops_per_sec = __this_cpu_read(cpu.info.loops_per_jiffy); + loops_per_sec *= HZ; + + /* How many clocks in a microsecond?, avoiding long long divide */ + work = loops_per_sec; + work *= 4295; /* 4295 = 2^32 / 1e6 */ + conv_us_to_clocks = (work >> 32); + + /* + * Carrier period in clocks, approach good up to 32GHz clock, + * gets carrier frequency within 8Hz + */ + period = loops_per_sec >> 3; + period /= (freq >> 3); + + /* Derive pulse and space from the period */ + pulse_width = period * duty_cycle / 100; + space_width = period - pulse_width; + dprintk("in init_timing_params, freq=%d, duty_cycle=%d, " + "clk/jiffy=%ld, pulse=%ld, space=%ld, " + "conv_us_to_clocks=%ld\n", + freq, duty_cycle, __this_cpu_read(cpu_info.loops_per_jiffy), + pulse_width, space_width, conv_us_to_clocks); + return 0; +} +#else /* ! USE_RDTSC */ +static int init_timing_params(unsigned int new_duty_cycle, + unsigned int new_freq) +{ +/* + * period, pulse/space width are kept with 8 binary places - + * IE multiplied by 256. + */ + if (256 * 1000000L / new_freq * new_duty_cycle / 100 <= + LIRC_SERIAL_TRANSMITTER_LATENCY) + return -EINVAL; + if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <= + LIRC_SERIAL_TRANSMITTER_LATENCY) + return -EINVAL; + duty_cycle = new_duty_cycle; + freq = new_freq; + period = 256 * 1000000L / freq; + pulse_width = period * duty_cycle / 100; + space_width = period - pulse_width; + dprintk("in init_timing_params, freq=%d pulse=%ld, " + "space=%ld\n", freq, pulse_width, space_width); + return 0; +} +#endif /* USE_RDTSC */ + + +/* return value: space length delta */ + +static long send_pulse_irdeo(unsigned long length) +{ + long rawbits, ret; + int i; + unsigned char output; + unsigned char chunk, shifted; + + /* how many bits have to be sent ? */ + rawbits = length * 1152 / 10000; + if (duty_cycle > 50) + chunk = 3; + else + chunk = 1; + for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) { + shifted = chunk << (i * 3); + shifted >>= 1; + output &= (~shifted); + i++; + if (i == 3) { + soutp(UART_TX, output); + while (!(sinp(UART_LSR) & UART_LSR_THRE)) + ; + output = 0x7f; + i = 0; + } + } + if (i != 0) { + soutp(UART_TX, output); + while (!(sinp(UART_LSR) & UART_LSR_TEMT)) + ; + } + + if (i == 0) + ret = (-rawbits) * 10000 / 1152; + else + ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152; + + return ret; +} + +#ifdef USE_RDTSC +/* Version that uses Pentium rdtsc instruction to measure clocks */ + +/* + * This version does sub-microsecond timing using rdtsc instruction, + * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY + * Implicitly i586 architecture... - Steve + */ + +static long send_pulse_homebrew_softcarrier(unsigned long length) +{ + int flag; + unsigned long target, start, now; + + /* Get going quick as we can */ + rdtscl(start); + on(); + /* Convert length from microseconds to clocks */ + length *= conv_us_to_clocks; + /* And loop till time is up - flipping at right intervals */ + now = start; + target = pulse_width; + flag = 1; + /* + * FIXME: This looks like a hard busy wait, without even an occasional, + * polite, cpu_relax() call. There's got to be a better way? + * + * The i2c code has the result of a lot of bit-banging work, I wonder if + * there's something there which could be helpful here. + */ + while ((now - start) < length) { + /* Delay till flip time */ + do { + rdtscl(now); + } while ((now - start) < target); + + /* flip */ + if (flag) { + rdtscl(now); + off(); + target += space_width; + } else { + rdtscl(now); on(); + target += pulse_width; + } + flag = !flag; + } + rdtscl(now); + return ((now - start) - length) / conv_us_to_clocks; +} +#else /* ! USE_RDTSC */ +/* Version using udelay() */ + +/* + * here we use fixed point arithmetic, with 8 + * fractional bits. that gets us within 0.1% or so of the right average + * frequency, albeit with some jitter in pulse length - Steve + */ + +/* To match 8 fractional bits used for pulse/space length */ + +static long send_pulse_homebrew_softcarrier(unsigned long length) +{ + int flag; + unsigned long actual, target, d; + length <<= 8; + + actual = 0; target = 0; flag = 0; + while (actual < length) { + if (flag) { + off(); + target += space_width; + } else { + on(); + target += pulse_width; + } + d = (target - actual - + LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8; + /* + * Note - we've checked in ioctl that the pulse/space + * widths are big enough so that d is > 0 + */ + udelay(d); + actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY; + flag = !flag; + } + return (actual-length) >> 8; +} +#endif /* USE_RDTSC */ + +static long send_pulse_homebrew(unsigned long length) +{ + if (length <= 0) + return 0; + + if (softcarrier) + return send_pulse_homebrew_softcarrier(length); + else { + on(); + safe_udelay(length); + return 0; + } +} + +static void send_space_irdeo(long length) +{ + if (length <= 0) + return; + + safe_udelay(length); +} + +static void send_space_homebrew(long length) +{ + off(); + if (length <= 0) + return; + safe_udelay(length); +} + +static void rbwrite(int l) +{ + if (lirc_buffer_full(&rbuf)) { + /* no new signals will be accepted */ + dprintk("Buffer overrun\n"); + return; + } + lirc_buffer_write(&rbuf, (void *)&l); +} + +static void frbwrite(int l) +{ + /* simple noise filter */ + static int pulse, space; + static unsigned int ptr; + + if (ptr > 0 && (l & PULSE_BIT)) { + pulse += l & PULSE_MASK; + if (pulse > 250) { + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + return; + } + if (!(l & PULSE_BIT)) { + if (ptr == 0) { + if (l > 20000) { + space = l; + ptr++; + return; + } + } else { + if (l > 20000) { + space += pulse; + if (space > PULSE_MASK) + space = PULSE_MASK; + space += l; + if (space > PULSE_MASK) + space = PULSE_MASK; + pulse = 0; + return; + } + rbwrite(space); + rbwrite(pulse | PULSE_BIT); + ptr = 0; + pulse = 0; + } + } + rbwrite(l); +} + +static irqreturn_t irq_handler(int i, void *blah) +{ + struct timeval tv; + int counter, dcd; + u8 status; + long deltv; + int data; + static int last_dcd = -1; + + if ((sinp(UART_IIR) & UART_IIR_NO_INT)) { + /* not our interrupt */ + return IRQ_NONE; + } + + counter = 0; + do { + counter++; + status = sinp(UART_MSR); + if (counter > RS_ISR_PASS_LIMIT) { + printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: " + "We're caught!\n"); + break; + } + if ((status & hardware[type].signal_pin_change) + && sense != -1) { + /* get current time */ + do_gettimeofday(&tv); + + /* New mode, written by Trent Piepho + . */ + + /* + * The old format was not very portable. + * We now use an int to pass pulses + * and spaces to user space. + * + * If PULSE_BIT is set a pulse has been + * received, otherwise a space has been + * received. The driver needs to know if your + * receiver is active high or active low, or + * the space/pulse sense could be + * inverted. The bits denoted by PULSE_MASK are + * the length in microseconds. Lengths greater + * than or equal to 16 seconds are clamped to + * PULSE_MASK. All other bits are unused. + * This is a much simpler interface for user + * programs, as well as eliminating "out of + * phase" errors with space/pulse + * autodetection. + */ + + /* calc time since last interrupt in microseconds */ + dcd = (status & hardware[type].signal_pin) ? 1 : 0; + + if (dcd == last_dcd) { + printk(KERN_WARNING LIRC_DRIVER_NAME + ": ignoring spike: %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + continue; + } + + deltv = tv.tv_sec-lasttv.tv_sec; + if (tv.tv_sec < lasttv.tv_sec || + (tv.tv_sec == lasttv.tv_sec && + tv.tv_usec < lasttv.tv_usec)) { + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: your clock just jumped " + "backwards\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": %d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + data = PULSE_MASK; + } else if (deltv > 15) { + data = PULSE_MASK; /* really long time */ + if (!(dcd^sense)) { + /* sanity check */ + printk(KERN_WARNING LIRC_DRIVER_NAME + ": AIEEEE: " + "%d %d %lx %lx %lx %lx\n", + dcd, sense, + tv.tv_sec, lasttv.tv_sec, + tv.tv_usec, lasttv.tv_usec); + /* + * detecting pulse while this + * MUST be a space! + */ + sense = sense ? 0 : 1; + } + } else + data = (int) (deltv*1000000 + + tv.tv_usec - + lasttv.tv_usec); + frbwrite(dcd^sense ? data : (data|PULSE_BIT)); + lasttv = tv; + last_dcd = dcd; + wake_up_interruptible(&rbuf.wait_poll); + } + } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */ + return IRQ_HANDLED; +} + + +static int hardware_init_port(void) +{ + u8 scratch, scratch2, scratch3; + + /* + * This is a simple port existence test, borrowed from the autoconfig + * function in drivers/serial/8250.c + */ + scratch = sinp(UART_IER); + soutp(UART_IER, 0); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = sinp(UART_IER) & 0x0f; + soutp(UART_IER, 0x0f); +#ifdef __i386__ + outb(0x00, 0x080); +#endif + scratch3 = sinp(UART_IER) & 0x0f; + soutp(UART_IER, scratch); + if (scratch2 != 0 || scratch3 != 0x0f) { + /* we fail, there's nothing here */ + printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test " + "failed, cannot continue\n"); + return -EINVAL; + } + + + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* First of all, disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + + /* Clear registers. */ + sinp(UART_LSR); + sinp(UART_RX); + sinp(UART_IIR); + sinp(UART_MSR); + +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + if (type == LIRC_NSLU2) { + /* Setup NSLU2 UART */ + + /* Enable UART */ + soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE); + /* Disable Receiver data Time out interrupt */ + soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE); + /* set out2 = interrupt unmask; off() doesn't set MCR + on NSLU2 */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + } +#endif + + /* Set line for power source */ + off(); + + /* Clear registers again to be sure. */ + sinp(UART_LSR); + sinp(UART_RX); + sinp(UART_IIR); + sinp(UART_MSR); + + switch (type) { + case LIRC_IRDEO: + case LIRC_IRDEO_REMOTE: + /* setup port to 7N1 @ 115200 Baud */ + /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */ + + /* Set DLAB 1. */ + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + /* Set DLAB 0 + 7N1 */ + soutp(UART_LCR, UART_LCR_WLEN7); + /* THR interrupt already disabled at this point */ + break; + default: + break; + } + + return 0; +} + +static int init_port(void) +{ + int i, nlow, nhigh, result; + + result = request_irq(irq, irq_handler, + (share_irq ? IRQF_SHARED : 0), + LIRC_DRIVER_NAME, (void *)&hardware); + + switch (result) { + case -EBUSY: + printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq); + return -EBUSY; + case -EINVAL: + printk(KERN_ERR LIRC_DRIVER_NAME + ": Bad irq number or handler\n"); + return -EINVAL; + default: + break; + }; + + /* Reserve io region. */ + /* + * Future MMAP-Developers: Attention! + * For memory mapped I/O you *might* need to use ioremap() first, + * for the NSLU2 it's done in boot code. + */ + if (((iommap != 0) + && (request_mem_region(iommap, 8 << ioshift, + LIRC_DRIVER_NAME) == NULL)) + || ((iommap == 0) + && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": port %04x already in use\n", io); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": use 'setserial /dev/ttySX uart none'\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": or compile the serial port driver as module and\n"); + printk(KERN_WARNING LIRC_DRIVER_NAME + ": make sure this module is loaded first\n"); + return -EBUSY; + } + + if (hardware_init_port() < 0) + return -EINVAL; + + /* Initialize pulse/space widths */ + init_timing_params(duty_cycle, freq); + + /* If pin is high, then this must be an active low receiver. */ + if (sense == -1) { + /* wait 1/2 sec for the power supply */ + msleep(500); + + /* + * probe 9 times every 0.04s, collect "votes" for + * active high/low + */ + nlow = 0; + nhigh = 0; + for (i = 0; i < 9; i++) { + if (sinp(UART_MSR) & hardware[type].signal_pin) + nlow++; + else + nhigh++; + msleep(40); + } + sense = (nlow >= nhigh ? 1 : 0); + printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active " + "%s receiver\n", sense ? "low" : "high"); + } else + printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active " + "%s receiver\n", sense ? "low" : "high"); + + dprintk("Interrupt %d, port %04x obtained\n", irq, io); + return 0; +} + +static int set_use_inc(void *data) +{ + unsigned long flags; + + /* initialize timestamp */ + do_gettimeofday(&lasttv); + + spin_lock_irqsave(&hardware[type].lock, flags); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); + + spin_unlock_irqrestore(&hardware[type].lock, flags); + + return 0; +} + +static void set_use_dec(void *data) +{ unsigned long flags; + + spin_lock_irqsave(&hardware[type].lock, flags); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* First of all, disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + spin_unlock_irqrestore(&hardware[type].lock, flags); +} + +static ssize_t lirc_write(struct file *file, const char *buf, + size_t n, loff_t *ppos) +{ + int i, count; + unsigned long flags; + long delta = 0; + int *wbuf; + + if (!(hardware[type].features & LIRC_CAN_SEND_PULSE)) + return -EBADF; + + count = n / sizeof(int); + if (n % sizeof(int) || count % 2 == 0) + return -EINVAL; + wbuf = memdup_user(buf, n); + if (IS_ERR(wbuf)) + return PTR_ERR(wbuf); + spin_lock_irqsave(&hardware[type].lock, flags); + if (type == LIRC_IRDEO) { + /* DTR, RTS down */ + on(); + } + for (i = 0; i < count; i++) { + if (i%2) + hardware[type].send_space(wbuf[i] - delta); + else + delta = hardware[type].send_pulse(wbuf[i]); + } + off(); + spin_unlock_irqrestore(&hardware[type].lock, flags); + kfree(wbuf); + return n; +} + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int result; + __u32 value; + + switch (cmd) { + case LIRC_GET_SEND_MODE: + if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) + return -ENOIOCTLCMD; + + result = put_user(LIRC_SEND2MODE + (hardware[type].features&LIRC_CAN_SEND_MASK), + (__u32 *) arg); + if (result) + return result; + break; + + case LIRC_SET_SEND_MODE: + if (!(hardware[type].features&LIRC_CAN_SEND_MASK)) + return -ENOIOCTLCMD; + + result = get_user(value, (__u32 *) arg); + if (result) + return result; + /* only LIRC_MODE_PULSE supported */ + if (value != LIRC_MODE_PULSE) + return -ENOSYS; + break; + + case LIRC_GET_LENGTH: + return -ENOSYS; + break; + + case LIRC_SET_SEND_DUTY_CYCLE: + dprintk("SET_SEND_DUTY_CYCLE\n"); + if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE)) + return -ENOIOCTLCMD; + + result = get_user(value, (__u32 *) arg); + if (result) + return result; + if (value <= 0 || value > 100) + return -EINVAL; + return init_timing_params(value, freq); + break; + + case LIRC_SET_SEND_CARRIER: + dprintk("SET_SEND_CARRIER\n"); + if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER)) + return -ENOIOCTLCMD; + + result = get_user(value, (__u32 *) arg); + if (result) + return result; + if (value > 500000 || value < 20000) + return -EINVAL; + return init_timing_params(duty_cycle, value); + break; + + default: + return lirc_dev_fop_ioctl(filep, cmd, arg); + } + return 0; +} + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .write = lirc_write, + .unlocked_ioctl = lirc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lirc_ioctl, +#endif + .read = lirc_dev_fop_read, + .poll = lirc_dev_fop_poll, + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, + .llseek = no_llseek, +}; + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .rbuf = &rbuf, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + +static struct platform_device *lirc_serial_dev; + +static int __devinit lirc_serial_probe(struct platform_device *dev) +{ + return 0; +} + +static int __devexit lirc_serial_remove(struct platform_device *dev) +{ + return 0; +} + +static int lirc_serial_suspend(struct platform_device *dev, + pm_message_t state) +{ + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* Disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + + /* Clear registers. */ + sinp(UART_LSR); + sinp(UART_RX); + sinp(UART_IIR); + sinp(UART_MSR); + + return 0; +} + +/* twisty maze... need a forward-declaration here... */ +static void lirc_serial_exit(void); + +static int lirc_serial_resume(struct platform_device *dev) +{ + unsigned long flags; + + if (hardware_init_port() < 0) { + lirc_serial_exit(); + return -EINVAL; + } + + spin_lock_irqsave(&hardware[type].lock, flags); + /* Enable Interrupt */ + do_gettimeofday(&lasttv); + soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI); + off(); + + lirc_buffer_clear(&rbuf); + + spin_unlock_irqrestore(&hardware[type].lock, flags); + + return 0; +} + +static struct platform_driver lirc_serial_driver = { + .probe = lirc_serial_probe, + .remove = __devexit_p(lirc_serial_remove), + .suspend = lirc_serial_suspend, + .resume = lirc_serial_resume, + .driver = { + .name = "lirc_serial", + .owner = THIS_MODULE, + }, +}; + +static int __init lirc_serial_init(void) +{ + int result; + + /* Init read buffer. */ + result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN); + if (result < 0) + return -ENOMEM; + + result = platform_driver_register(&lirc_serial_driver); + if (result) { + printk("lirc register returned %d\n", result); + goto exit_buffer_free; + } + + lirc_serial_dev = platform_device_alloc("lirc_serial", 0); + if (!lirc_serial_dev) { + result = -ENOMEM; + goto exit_driver_unregister; + } + + result = platform_device_add(lirc_serial_dev); + if (result) + goto exit_device_put; + + return 0; + +exit_device_put: + platform_device_put(lirc_serial_dev); +exit_driver_unregister: + platform_driver_unregister(&lirc_serial_driver); +exit_buffer_free: + lirc_buffer_free(&rbuf); + return result; +} + +static void lirc_serial_exit(void) +{ + platform_device_unregister(lirc_serial_dev); + platform_driver_unregister(&lirc_serial_driver); + lirc_buffer_free(&rbuf); +} + +static int __init lirc_serial_init_module(void) +{ + int result; + + result = lirc_serial_init(); + if (result) + return result; + + switch (type) { + case LIRC_HOMEBREW: + case LIRC_IRDEO: + case LIRC_IRDEO_REMOTE: + case LIRC_ANIMAX: + case LIRC_IGOR: + /* if nothing specified, use ttyS0/com1 and irq 4 */ + io = io ? io : 0x3f8; + irq = irq ? irq : 4; + break; +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + case LIRC_NSLU2: + io = io ? io : IRQ_IXP4XX_UART2; + irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET); + iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS; + ioshift = ioshift ? ioshift : 2; + break; +#endif + default: + result = -EINVAL; + goto exit_serial_exit; + } + if (!softcarrier) { + switch (type) { + case LIRC_HOMEBREW: + case LIRC_IGOR: +#ifdef CONFIG_LIRC_SERIAL_NSLU2 + case LIRC_NSLU2: +#endif + hardware[type].features &= + ~(LIRC_CAN_SET_SEND_DUTY_CYCLE| + LIRC_CAN_SET_SEND_CARRIER); + break; + } + } + + result = init_port(); + if (result < 0) + goto exit_serial_exit; + driver.features = hardware[type].features; + driver.dev = &lirc_serial_dev->dev; + driver.minor = lirc_register_driver(&driver); + if (driver.minor < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": register_chrdev failed!\n"); + result = -EIO; + goto exit_release; + } + return 0; +exit_release: + release_region(io, 8); +exit_serial_exit: + lirc_serial_exit(); + return result; +} + +static void __exit lirc_serial_exit_module(void) +{ + lirc_serial_exit(); + + free_irq(irq, (void *)&hardware); + + if (iommap != 0) + release_mem_region(iommap, 8 << ioshift); + else + release_region(io, 8); + lirc_unregister_driver(driver.minor); + dprintk("cleaned up module\n"); +} + + +module_init(lirc_serial_init_module); +module_exit(lirc_serial_exit_module); + +MODULE_DESCRIPTION("Infra-red receiver driver for serial ports."); +MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, " + "Christoph Bartelmus, Andrei Tanas"); +MODULE_LICENSE("GPL"); + +module_param(type, int, S_IRUGO); +MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo," + " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug," + " 5 = NSLU2 RX:CTS2/TX:GreenLED)"); + +module_param(io, int, S_IRUGO); +MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); + +/* some architectures (e.g. intel xscale) have memory mapped registers */ +module_param(iommap, bool, S_IRUGO); +MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O" + " (0 = no memory mapped io)"); + +/* + * some architectures (e.g. intel xscale) align the 8bit serial registers + * on 32bit word boundaries. + * See linux-kernel/serial/8250.c serial_in()/out() + */ +module_param(ioshift, int, S_IRUGO); +MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)"); + +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); + +module_param(share_irq, bool, S_IRUGO); +MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)"); + +module_param(sense, bool, S_IRUGO); +MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit" + " (0 = active high, 1 = active low )"); + +#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER +module_param(txsense, bool, S_IRUGO); +MODULE_PARM_DESC(txsense, "Sense of transmitter circuit" + " (0 = active high, 1 = active low )"); +#endif + +module_param(softcarrier, bool, S_IRUGO); +MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)"); + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/media/lirc/lirc_sir.c b/drivers/staging/media/lirc/lirc_sir.c new file mode 100644 index 000000000000..6903d3992eca --- /dev/null +++ b/drivers/staging/media/lirc/lirc_sir.c @@ -0,0 +1,1279 @@ +/* + * LIRC SIR driver, (C) 2000 Milan Pikula + * + * lirc_sir - Device driver for use with SIR (serial infra red) + * mode of IrDA on many notebooks. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * + * 2000/09/16 Frank Przybylski : + * added timeout and relaxed pulse detection, removed gap bug + * + * 2000/12/15 Christoph Bartelmus : + * added support for Tekram Irmate 210 (sending does not work yet, + * kind of disappointing that nobody was able to implement that + * before), + * major clean-up + * + * 2001/02/27 Christoph Bartelmus : + * added support for StrongARM SA1100 embedded microprocessor + * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef LIRC_ON_SA1100 +#include +#ifdef CONFIG_SA1100_COLLIE +#include +#include +#endif +#endif + +#include + +#include +#include + +/* SECTION: Definitions */ + +/*** Tekram dongle ***/ +#ifdef LIRC_SIR_TEKRAM +/* stolen from kernel source */ +/* definitions for Tekram dongle */ +#define TEKRAM_115200 0x00 +#define TEKRAM_57600 0x01 +#define TEKRAM_38400 0x02 +#define TEKRAM_19200 0x03 +#define TEKRAM_9600 0x04 +#define TEKRAM_2400 0x08 + +#define TEKRAM_PW 0x10 /* Pulse select bit */ + +/* 10bit * 1s/115200bit in milliseconds = 87ms*/ +#define TIME_CONST (10000000ul/115200ul) + +#endif + +#ifdef LIRC_SIR_ACTISYS_ACT200L +static void init_act200(void); +#elif defined(LIRC_SIR_ACTISYS_ACT220L) +static void init_act220(void); +#endif + +/*** SA1100 ***/ +#ifdef LIRC_ON_SA1100 +struct sa1100_ser2_registers { + /* HSSP control register */ + unsigned char hscr0; + /* UART registers */ + unsigned char utcr0; + unsigned char utcr1; + unsigned char utcr2; + unsigned char utcr3; + unsigned char utcr4; + unsigned char utdr; + unsigned char utsr0; + unsigned char utsr1; +} sr; + +static int irq = IRQ_Ser2ICP; + +#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0 + +/* pulse/space ratio of 50/50 */ +static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); +/* 1000000/freq-pulse_width */ +static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY); +static unsigned int freq = 38000; /* modulation frequency */ +static unsigned int duty_cycle = 50; /* duty cycle of 50% */ + +#endif + +#define RBUF_LEN 1024 +#define WBUF_LEN 1024 + +#define LIRC_DRIVER_NAME "lirc_sir" + +#define PULSE '[' + +#ifndef LIRC_SIR_TEKRAM +/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/ +#define TIME_CONST (9000000ul/115200ul) +#endif + + +/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */ +#define SIR_TIMEOUT (HZ*5/100) + +#ifndef LIRC_ON_SA1100 +#ifndef LIRC_IRQ +#define LIRC_IRQ 4 +#endif +#ifndef LIRC_PORT +/* for external dongles, default to com1 */ +#if defined(LIRC_SIR_ACTISYS_ACT200L) || \ + defined(LIRC_SIR_ACTISYS_ACT220L) || \ + defined(LIRC_SIR_TEKRAM) +#define LIRC_PORT 0x3f8 +#else +/* onboard sir ports are typically com3 */ +#define LIRC_PORT 0x3e8 +#endif +#endif + +static int io = LIRC_PORT; +static int irq = LIRC_IRQ; +static int threshold = 3; +#endif + +static DEFINE_SPINLOCK(timer_lock); +static struct timer_list timerlist; +/* time of last signal change detected */ +static struct timeval last_tv = {0, 0}; +/* time of last UART data ready interrupt */ +static struct timeval last_intr_tv = {0, 0}; +static int last_value; + +static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue); + +static DEFINE_SPINLOCK(hardware_lock); + +static int rx_buf[RBUF_LEN]; +static unsigned int rx_tail, rx_head; + +static int debug; +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \ + fmt, ## args); \ + } while (0) + +/* SECTION: Prototypes */ + +/* Communication with user-space */ +static unsigned int lirc_poll(struct file *file, poll_table *wait); +static ssize_t lirc_read(struct file *file, char *buf, size_t count, + loff_t *ppos); +static ssize_t lirc_write(struct file *file, const char *buf, size_t n, + loff_t *pos); +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg); +static void add_read_queue(int flag, unsigned long val); +static int init_chrdev(void); +static void drop_chrdev(void); +/* Hardware */ +static irqreturn_t sir_interrupt(int irq, void *dev_id); +static void send_space(unsigned long len); +static void send_pulse(unsigned long len); +static int init_hardware(void); +static void drop_hardware(void); +/* Initialisation */ +static int init_port(void); +static void drop_port(void); + +#ifdef LIRC_ON_SA1100 +static void on(void) +{ + PPSR |= PPC_TXD2; +} + +static void off(void) +{ + PPSR &= ~PPC_TXD2; +} +#else +static inline unsigned int sinp(int offset) +{ + return inb(io + offset); +} + +static inline void soutp(int offset, int value) +{ + outb(value, io + offset); +} +#endif + +#ifndef MAX_UDELAY_MS +#define MAX_UDELAY_US 5000 +#else +#define MAX_UDELAY_US (MAX_UDELAY_MS*1000) +#endif + +static void safe_udelay(unsigned long usecs) +{ + while (usecs > MAX_UDELAY_US) { + udelay(MAX_UDELAY_US); + usecs -= MAX_UDELAY_US; + } + udelay(usecs); +} + +/* SECTION: Communication with user-space */ + +static unsigned int lirc_poll(struct file *file, poll_table *wait) +{ + poll_wait(file, &lirc_read_queue, wait); + if (rx_head != rx_tail) + return POLLIN | POLLRDNORM; + return 0; +} + +static ssize_t lirc_read(struct file *file, char *buf, size_t count, + loff_t *ppos) +{ + int n = 0; + int retval = 0; + DECLARE_WAITQUEUE(wait, current); + + if (count % sizeof(int)) + return -EINVAL; + + add_wait_queue(&lirc_read_queue, &wait); + set_current_state(TASK_INTERRUPTIBLE); + while (n < count) { + if (rx_head != rx_tail) { + if (copy_to_user((void *) buf + n, + (void *) (rx_buf + rx_head), + sizeof(int))) { + retval = -EFAULT; + break; + } + rx_head = (rx_head + 1) & (RBUF_LEN - 1); + n += sizeof(int); + } else { + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } + } + remove_wait_queue(&lirc_read_queue, &wait); + set_current_state(TASK_RUNNING); + return n ? n : retval; +} +static ssize_t lirc_write(struct file *file, const char *buf, size_t n, + loff_t *pos) +{ + unsigned long flags; + int i, count; + int *tx_buf; + + count = n / sizeof(int); + if (n % sizeof(int) || count % 2 == 0) + return -EINVAL; + tx_buf = memdup_user(buf, n); + if (IS_ERR(tx_buf)) + return PTR_ERR(tx_buf); + i = 0; +#ifdef LIRC_ON_SA1100 + /* disable receiver */ + Ser2UTCR3 = 0; +#endif + local_irq_save(flags); + while (1) { + if (i >= count) + break; + if (tx_buf[i]) + send_pulse(tx_buf[i]); + i++; + if (i >= count) + break; + if (tx_buf[i]) + send_space(tx_buf[i]); + i++; + } + local_irq_restore(flags); +#ifdef LIRC_ON_SA1100 + off(); + udelay(1000); /* wait 1ms for IR diode to recover */ + Ser2UTCR3 = 0; + /* clear status register to prevent unwanted interrupts */ + Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + /* enable receiver */ + Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; +#endif + kfree(tx_buf); + return count; +} + +static long lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + int retval = 0; + __u32 value = 0; +#ifdef LIRC_ON_SA1100 + + if (cmd == LIRC_GET_FEATURES) + value = LIRC_CAN_SEND_PULSE | + LIRC_CAN_SET_SEND_DUTY_CYCLE | + LIRC_CAN_SET_SEND_CARRIER | + LIRC_CAN_REC_MODE2; + else if (cmd == LIRC_GET_SEND_MODE) + value = LIRC_MODE_PULSE; + else if (cmd == LIRC_GET_REC_MODE) + value = LIRC_MODE_MODE2; +#else + if (cmd == LIRC_GET_FEATURES) + value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2; + else if (cmd == LIRC_GET_SEND_MODE) + value = LIRC_MODE_PULSE; + else if (cmd == LIRC_GET_REC_MODE) + value = LIRC_MODE_MODE2; +#endif + + switch (cmd) { + case LIRC_GET_FEATURES: + case LIRC_GET_SEND_MODE: + case LIRC_GET_REC_MODE: + retval = put_user(value, (__u32 *) arg); + break; + + case LIRC_SET_SEND_MODE: + case LIRC_SET_REC_MODE: + retval = get_user(value, (__u32 *) arg); + break; +#ifdef LIRC_ON_SA1100 + case LIRC_SET_SEND_DUTY_CYCLE: + retval = get_user(value, (__u32 *) arg); + if (retval) + return retval; + if (value <= 0 || value > 100) + return -EINVAL; + /* (value/100)*(1000000/freq) */ + duty_cycle = value; + pulse_width = (unsigned long) duty_cycle*10000/freq; + space_width = (unsigned long) 1000000L/freq-pulse_width; + if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + break; + case LIRC_SET_SEND_CARRIER: + retval = get_user(value, (__u32 *) arg); + if (retval) + return retval; + if (value > 500000 || value < 20000) + return -EINVAL; + freq = value; + pulse_width = (unsigned long) duty_cycle*10000/freq; + space_width = (unsigned long) 1000000L/freq-pulse_width; + if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY) + space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY; + break; +#endif + default: + retval = -ENOIOCTLCMD; + + } + + if (retval) + return retval; + if (cmd == LIRC_SET_REC_MODE) { + if (value != LIRC_MODE_MODE2) + retval = -ENOSYS; + } else if (cmd == LIRC_SET_SEND_MODE) { + if (value != LIRC_MODE_PULSE) + retval = -ENOSYS; + } + + return retval; +} + +static void add_read_queue(int flag, unsigned long val) +{ + unsigned int new_rx_tail; + int newval; + + dprintk("add flag %d with val %lu\n", flag, val); + + newval = val & PULSE_MASK; + + /* + * statistically, pulses are ~TIME_CONST/2 too long. we could + * maybe make this more exact, but this is good enough + */ + if (flag) { + /* pulse */ + if (newval > TIME_CONST/2) + newval -= TIME_CONST/2; + else /* should not ever happen */ + newval = 1; + newval |= PULSE_BIT; + } else { + newval += TIME_CONST/2; + } + new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1); + if (new_rx_tail == rx_head) { + dprintk("Buffer overrun.\n"); + return; + } + rx_buf[rx_tail] = newval; + rx_tail = new_rx_tail; + wake_up_interruptible(&lirc_read_queue); +} + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .read = lirc_read, + .write = lirc_write, + .poll = lirc_poll, + .unlocked_ioctl = lirc_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = lirc_ioctl, +#endif + .open = lirc_dev_fop_open, + .release = lirc_dev_fop_close, + .llseek = no_llseek, +}; + +static int set_use_inc(void *data) +{ + return 0; +} + +static void set_use_dec(void *data) +{ +} + +static struct lirc_driver driver = { + .name = LIRC_DRIVER_NAME, + .minor = -1, + .code_length = 1, + .sample_rate = 0, + .data = NULL, + .add_to_buf = NULL, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .dev = NULL, + .owner = THIS_MODULE, +}; + + +static int init_chrdev(void) +{ + driver.minor = lirc_register_driver(&driver); + if (driver.minor < 0) { + printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n"); + return -EIO; + } + return 0; +} + +static void drop_chrdev(void) +{ + lirc_unregister_driver(driver.minor); +} + +/* SECTION: Hardware */ +static long delta(struct timeval *tv1, struct timeval *tv2) +{ + unsigned long deltv; + + deltv = tv2->tv_sec - tv1->tv_sec; + if (deltv > 15) + deltv = 0xFFFFFF; + else + deltv = deltv*1000000 + + tv2->tv_usec - + tv1->tv_usec; + return deltv; +} + +static void sir_timeout(unsigned long data) +{ + /* + * if last received signal was a pulse, but receiving stopped + * within the 9 bit frame, we need to finish this pulse and + * simulate a signal change to from pulse to space. Otherwise + * upper layers will receive two sequences next time. + */ + + unsigned long flags; + unsigned long pulse_end; + + /* avoid interference with interrupt */ + spin_lock_irqsave(&timer_lock, flags); + if (last_value) { +#ifndef LIRC_ON_SA1100 + /* clear unread bits in UART and restart */ + outb(UART_FCR_CLEAR_RCVR, io + UART_FCR); +#endif + /* determine 'virtual' pulse end: */ + pulse_end = delta(&last_tv, &last_intr_tv); + dprintk("timeout add %d for %lu usec\n", last_value, pulse_end); + add_read_queue(last_value, pulse_end); + last_value = 0; + last_tv = last_intr_tv; + } + spin_unlock_irqrestore(&timer_lock, flags); +} + +static irqreturn_t sir_interrupt(int irq, void *dev_id) +{ + unsigned char data; + struct timeval curr_tv; + static unsigned long deltv; +#ifdef LIRC_ON_SA1100 + int status; + static int n; + + status = Ser2UTSR0; + /* + * Deal with any receive errors first. The bytes in error may be + * the only bytes in the receive FIFO, so we do this first. + */ + while (status & UTSR0_EIF) { + int bstat; + + if (debug) { + dprintk("EIF\n"); + bstat = Ser2UTSR1; + + if (bstat & UTSR1_FRE) + dprintk("frame error\n"); + if (bstat & UTSR1_ROR) + dprintk("receive fifo overrun\n"); + if (bstat & UTSR1_PRE) + dprintk("parity error\n"); + } + + bstat = Ser2UTDR; + n++; + status = Ser2UTSR0; + } + + if (status & (UTSR0_RFS | UTSR0_RID)) { + do_gettimeofday(&curr_tv); + deltv = delta(&last_tv, &curr_tv); + do { + data = Ser2UTDR; + dprintk("%d data: %u\n", n, (unsigned int) data); + n++; + } while (status & UTSR0_RID && /* do not empty fifo in order to + * get UTSR0_RID in any case */ + Ser2UTSR1 & UTSR1_RNE); /* data ready */ + + if (status&UTSR0_RID) { + add_read_queue(0 , deltv - n * TIME_CONST); /*space*/ + add_read_queue(1, n * TIME_CONST); /*pulse*/ + n = 0; + last_tv = curr_tv; + } + } + + if (status & UTSR0_TFS) + printk(KERN_ERR "transmit fifo not full, shouldn't happen\n"); + + /* We must clear certain bits. */ + status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + if (status) + Ser2UTSR0 = status; +#else + unsigned long deltintrtv; + unsigned long flags; + int iir, lsr; + + while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) { + switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */ + case UART_IIR_MSI: + (void) inb(io + UART_MSR); + break; + case UART_IIR_RLSI: + (void) inb(io + UART_LSR); + break; + case UART_IIR_THRI: +#if 0 + if (lsr & UART_LSR_THRE) /* FIFO is empty */ + outb(data, io + UART_TX) +#endif + break; + case UART_IIR_RDI: + /* avoid interference with timer */ + spin_lock_irqsave(&timer_lock, flags); + do { + del_timer(&timerlist); + data = inb(io + UART_RX); + do_gettimeofday(&curr_tv); + deltv = delta(&last_tv, &curr_tv); + deltintrtv = delta(&last_intr_tv, &curr_tv); + dprintk("t %lu, d %d\n", deltintrtv, (int)data); + /* + * if nothing came in last X cycles, + * it was gap + */ + if (deltintrtv > TIME_CONST * threshold) { + if (last_value) { + dprintk("GAP\n"); + /* simulate signal change */ + add_read_queue(last_value, + deltv - + deltintrtv); + last_value = 0; + last_tv.tv_sec = + last_intr_tv.tv_sec; + last_tv.tv_usec = + last_intr_tv.tv_usec; + deltv = deltintrtv; + } + } + data = 1; + if (data ^ last_value) { + /* + * deltintrtv > 2*TIME_CONST, remember? + * the other case is timeout + */ + add_read_queue(last_value, + deltv-TIME_CONST); + last_value = data; + last_tv = curr_tv; + if (last_tv.tv_usec >= TIME_CONST) { + last_tv.tv_usec -= TIME_CONST; + } else { + last_tv.tv_sec--; + last_tv.tv_usec += 1000000 - + TIME_CONST; + } + } + last_intr_tv = curr_tv; + if (data) { + /* + * start timer for end of + * sequence detection + */ + timerlist.expires = jiffies + + SIR_TIMEOUT; + add_timer(&timerlist); + } + + lsr = inb(io + UART_LSR); + } while (lsr & UART_LSR_DR); /* data ready */ + spin_unlock_irqrestore(&timer_lock, flags); + break; + default: + break; + } + } +#endif + return IRQ_RETVAL(IRQ_HANDLED); +} + +#ifdef LIRC_ON_SA1100 +static void send_pulse(unsigned long length) +{ + unsigned long k, delay; + int flag; + + if (length == 0) + return; + /* + * this won't give us the carrier frequency we really want + * due to integer arithmetic, but we can accept this inaccuracy + */ + + for (k = flag = 0; k < length; k += delay, flag = !flag) { + if (flag) { + off(); + delay = space_width; + } else { + on(); + delay = pulse_width; + } + safe_udelay(delay); + } + off(); +} + +static void send_space(unsigned long length) +{ + if (length == 0) + return; + off(); + safe_udelay(length); +} +#else +static void send_space(unsigned long len) +{ + safe_udelay(len); +} + +static void send_pulse(unsigned long len) +{ + long bytes_out = len / TIME_CONST; + + if (bytes_out == 0) + bytes_out++; + + while (bytes_out--) { + outb(PULSE, io + UART_TX); + /* FIXME treba seriozne cakanie z char/serial.c */ + while (!(inb(io + UART_LSR) & UART_LSR_THRE)) + ; + } +} +#endif + +#ifdef CONFIG_SA1100_COLLIE +static int sa1100_irda_set_power_collie(int state) +{ + if (state) { + /* + * 0 - off + * 1 - short range, lowest power + * 2 - medium range, medium power + * 3 - maximum range, high power + */ + ucb1200_set_io_direction(TC35143_GPIO_IR_ON, + TC35143_IODIR_OUTPUT); + ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW); + udelay(100); + } else { + /* OFF */ + ucb1200_set_io_direction(TC35143_GPIO_IR_ON, + TC35143_IODIR_OUTPUT); + ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH); + } + return 0; +} +#endif + +static int init_hardware(void) +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + /* reset UART */ +#ifdef LIRC_ON_SA1100 +#ifdef CONFIG_SA1100_BITSY + if (machine_is_bitsy()) { + printk(KERN_INFO "Power on IR module\n"); + set_bitsy_egpio(EGPIO_BITSY_IR_ON); + } +#endif +#ifdef CONFIG_SA1100_COLLIE + sa1100_irda_set_power_collie(3); /* power on */ +#endif + sr.hscr0 = Ser2HSCR0; + + sr.utcr0 = Ser2UTCR0; + sr.utcr1 = Ser2UTCR1; + sr.utcr2 = Ser2UTCR2; + sr.utcr3 = Ser2UTCR3; + sr.utcr4 = Ser2UTCR4; + + sr.utdr = Ser2UTDR; + sr.utsr0 = Ser2UTSR0; + sr.utsr1 = Ser2UTSR1; + + /* configure GPIO */ + /* output */ + PPDR |= PPC_TXD2; + PSDR |= PPC_TXD2; + /* set output to 0 */ + off(); + + /* Enable HP-SIR modulation, and ensure that the port is disabled. */ + Ser2UTCR3 = 0; + Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP); + + /* clear status register to prevent unwanted interrupts */ + Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + + /* 7N1 */ + Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData; + /* 115200 */ + Ser2UTCR1 = 0; + Ser2UTCR2 = 1; + /* use HPSIR, 1.6 usec pulses */ + Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us; + + /* enable receiver, receive fifo interrupt */ + Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE; + + /* clear status register to prevent unwanted interrupts */ + Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB); + +#elif defined(LIRC_SIR_TEKRAM) + /* disable FIFO */ + soutp(UART_FCR, + UART_FCR_CLEAR_RCVR| + UART_FCR_CLEAR_XMIT| + UART_FCR_TRIGGER_1); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* First of all, disable all interrupts */ + soutp(UART_IER, sinp(UART_IER) & + (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI))); + + /* Set DLAB 1. */ + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + + /* Set divisor to 12 => 9600 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 12); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* power supply */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + safe_udelay(50*1000); + + /* -DTR low -> reset PIC */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + udelay(1*1000); + + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(100); + + + /* -RTS low -> send control byte */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); + udelay(7); + soutp(UART_TX, TEKRAM_115200|TEKRAM_PW); + + /* one byte takes ~1042 usec to transmit at 9600,8N1 */ + udelay(1500); + + /* back to normal operation */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(50); + + udelay(1500); + + /* read previous control byte */ + printk(KERN_INFO LIRC_DRIVER_NAME + ": 0x%02x\n", sinp(UART_RX)); + + /* Set DLAB 1. */ + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + + /* Set DLAB 0, 8 Bit */ + soutp(UART_LCR, UART_LCR_WLEN8); + /* enable interrupts */ + soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); +#else + outb(0, io + UART_MCR); + outb(0, io + UART_IER); + /* init UART */ + /* set DLAB, speed = 115200 */ + outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR); + outb(1, io + UART_DLL); outb(0, io + UART_DLM); + /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */ + outb(UART_LCR_WLEN7, io + UART_LCR); + /* FIFO operation */ + outb(UART_FCR_ENABLE_FIFO, io + UART_FCR); + /* interrupts */ + /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */ + outb(UART_IER_RDI, io + UART_IER); + /* turn on UART */ + outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR); +#ifdef LIRC_SIR_ACTISYS_ACT200L + init_act200(); +#elif defined(LIRC_SIR_ACTISYS_ACT220L) + init_act220(); +#endif +#endif + spin_unlock_irqrestore(&hardware_lock, flags); + return 0; +} + +static void drop_hardware(void) +{ + unsigned long flags; + + spin_lock_irqsave(&hardware_lock, flags); + +#ifdef LIRC_ON_SA1100 + Ser2UTCR3 = 0; + + Ser2UTCR0 = sr.utcr0; + Ser2UTCR1 = sr.utcr1; + Ser2UTCR2 = sr.utcr2; + Ser2UTCR4 = sr.utcr4; + Ser2UTCR3 = sr.utcr3; + + Ser2HSCR0 = sr.hscr0; +#ifdef CONFIG_SA1100_BITSY + if (machine_is_bitsy()) + clr_bitsy_egpio(EGPIO_BITSY_IR_ON); +#endif +#ifdef CONFIG_SA1100_COLLIE + sa1100_irda_set_power_collie(0); /* power off */ +#endif +#else + /* turn off interrupts */ + outb(0, io + UART_IER); +#endif + spin_unlock_irqrestore(&hardware_lock, flags); +} + +/* SECTION: Initialisation */ + +static int init_port(void) +{ + int retval; + + /* get I/O port access and IRQ line */ +#ifndef LIRC_ON_SA1100 + if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) { + printk(KERN_ERR LIRC_DRIVER_NAME + ": i/o port 0x%.4x already in use.\n", io); + return -EBUSY; + } +#endif + retval = request_irq(irq, sir_interrupt, 0, + LIRC_DRIVER_NAME, NULL); + if (retval < 0) { +# ifndef LIRC_ON_SA1100 + release_region(io, 8); +# endif + printk(KERN_ERR LIRC_DRIVER_NAME + ": IRQ %d already in use.\n", + irq); + return retval; + } +#ifndef LIRC_ON_SA1100 + printk(KERN_INFO LIRC_DRIVER_NAME + ": I/O port 0x%.4x, IRQ %d.\n", + io, irq); +#endif + + init_timer(&timerlist); + timerlist.function = sir_timeout; + timerlist.data = 0xabadcafe; + + return 0; +} + +static void drop_port(void) +{ + free_irq(irq, NULL); + del_timer_sync(&timerlist); +#ifndef LIRC_ON_SA1100 + release_region(io, 8); +#endif +} + +#ifdef LIRC_SIR_ACTISYS_ACT200L +/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */ +/* some code borrowed from Linux IRDA driver */ + +/* Register 0: Control register #1 */ +#define ACT200L_REG0 0x00 +#define ACT200L_TXEN 0x01 /* Enable transmitter */ +#define ACT200L_RXEN 0x02 /* Enable receiver */ +#define ACT200L_ECHO 0x08 /* Echo control chars */ + +/* Register 1: Control register #2 */ +#define ACT200L_REG1 0x10 +#define ACT200L_LODB 0x01 /* Load new baud rate count value */ +#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */ + +/* Register 3: Transmit mode register #2 */ +#define ACT200L_REG3 0x30 +#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ +#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */ +#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */ + +/* Register 4: Output Power register */ +#define ACT200L_REG4 0x40 +#define ACT200L_OP0 0x01 /* Enable LED1C output */ +#define ACT200L_OP1 0x02 /* Enable LED2C output */ +#define ACT200L_BLKR 0x04 + +/* Register 5: Receive Mode register */ +#define ACT200L_REG5 0x50 +#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */ + /*.. other various IRDA bit modes, and TV remote modes..*/ + +/* Register 6: Receive Sensitivity register #1 */ +#define ACT200L_REG6 0x60 +#define ACT200L_RS0 0x01 /* receive threshold bit 0 */ +#define ACT200L_RS1 0x02 /* receive threshold bit 1 */ + +/* Register 7: Receive Sensitivity register #2 */ +#define ACT200L_REG7 0x70 +#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */ + +/* Register 8,9: Baud Rate Divider register #1,#2 */ +#define ACT200L_REG8 0x80 +#define ACT200L_REG9 0x90 + +#define ACT200L_2400 0x5f +#define ACT200L_9600 0x17 +#define ACT200L_19200 0x0b +#define ACT200L_38400 0x05 +#define ACT200L_57600 0x03 +#define ACT200L_115200 0x01 + +/* Register 13: Control register #3 */ +#define ACT200L_REG13 0xd0 +#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */ + +/* Register 15: Status register */ +#define ACT200L_REG15 0xf0 + +/* Register 21: Control register #4 */ +#define ACT200L_REG21 0x50 +#define ACT200L_EXCK 0x02 /* Disable clock output driver */ +#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */ + +static void init_act200(void) +{ + int i; + __u8 control[] = { + ACT200L_REG15, + ACT200L_REG13 | ACT200L_SHDW, + ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL, + ACT200L_REG13, + ACT200L_REG7 | ACT200L_ENPOS, + ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1, + ACT200L_REG5 | ACT200L_RWIDL, + ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR, + ACT200L_REG3 | ACT200L_B0, + ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN, + ACT200L_REG8 | (ACT200L_115200 & 0x0f), + ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f), + ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE + }; + + /* Set DLAB 1. */ + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8); + + /* Set divisor to 12 => 9600 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 12); + + /* Set DLAB 0. */ + soutp(UART_LCR, UART_LCR_WLEN8); + /* Set divisor to 12 => 9600 Baud */ + + /* power supply */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + for (i = 0; i < 50; i++) + safe_udelay(1000); + + /* Reset the dongle : set RTS low for 25 ms */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); + for (i = 0; i < 25; i++) + udelay(1000); + + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(100); + + /* Clear DTR and set RTS to enter command mode */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + udelay(7); + + /* send out the control register settings for 115K 7N1 SIR operation */ + for (i = 0; i < sizeof(control); i++) { + soutp(UART_TX, control[i]); + /* one byte takes ~1042 usec to transmit at 9600,8N1 */ + udelay(1500); + } + + /* back to normal operation */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + udelay(50); + + udelay(1500); + soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB); + + /* Set DLAB 1. */ + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7); + + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + + /* Set DLAB 0. */ + soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB)); + + /* Set DLAB 0, 7 Bit */ + soutp(UART_LCR, UART_LCR_WLEN7); + + /* enable interrupts */ + soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI); +} +#endif + +#ifdef LIRC_SIR_ACTISYS_ACT220L +/* + * Derived from linux IrDA driver (net/irda/actisys.c) + * Drop me a mail for any kind of comment: maxx@spaceboyz.net + */ + +void init_act220(void) +{ + int i; + + /* DLAB 1 */ + soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7); + + /* 9600 baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 12); + + /* DLAB 0 */ + soutp(UART_LCR, UART_LCR_WLEN7); + + /* reset the dongle, set DTR low for 10us */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2); + udelay(10); + + /* back to normal (still 9600) */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2); + + /* + * send RTS pulses until we reach 115200 + * i hope this is really the same for act220l/act220l+ + */ + for (i = 0; i < 3; i++) { + udelay(10); + /* set RTS low for 10 us */ + soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2); + udelay(10); + /* set RTS high for 10 us */ + soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2); + } + + /* back to normal operation */ + udelay(1500); /* better safe than sorry ;) */ + + /* Set DLAB 1. */ + soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7); + + /* Set divisor to 1 => 115200 Baud */ + soutp(UART_DLM, 0); + soutp(UART_DLL, 1); + + /* Set DLAB 0, 7 Bit */ + /* The dongle doesn't seem to have any problems with operation at 7N1 */ + soutp(UART_LCR, UART_LCR_WLEN7); + + /* enable interrupts */ + soutp(UART_IER, UART_IER_RDI); +} +#endif + +static int init_lirc_sir(void) +{ + int retval; + + init_waitqueue_head(&lirc_read_queue); + retval = init_port(); + if (retval < 0) + return retval; + init_hardware(); + printk(KERN_INFO LIRC_DRIVER_NAME + ": Installed.\n"); + return 0; +} + + +static int __init lirc_sir_init(void) +{ + int retval; + + retval = init_chrdev(); + if (retval < 0) + return retval; + retval = init_lirc_sir(); + if (retval) { + drop_chrdev(); + return retval; + } + return 0; +} + +static void __exit lirc_sir_exit(void) +{ + drop_hardware(); + drop_chrdev(); + drop_port(); + printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n"); +} + +module_init(lirc_sir_init); +module_exit(lirc_sir_exit); + +#ifdef LIRC_SIR_TEKRAM +MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210"); +MODULE_AUTHOR("Christoph Bartelmus"); +#elif defined(LIRC_ON_SA1100) +MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor"); +MODULE_AUTHOR("Christoph Bartelmus"); +#elif defined(LIRC_SIR_ACTISYS_ACT200L) +MODULE_DESCRIPTION("LIRC driver for Actisys Act200L"); +MODULE_AUTHOR("Karl Bongers"); +#elif defined(LIRC_SIR_ACTISYS_ACT220L) +MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)"); +MODULE_AUTHOR("Jan Roemisch"); +#else +MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports"); +MODULE_AUTHOR("Milan Pikula"); +#endif +MODULE_LICENSE("GPL"); + +#ifdef LIRC_ON_SA1100 +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (16)"); +#else +module_param(io, int, S_IRUGO); +MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)"); + +module_param(irq, int, S_IRUGO); +MODULE_PARM_DESC(irq, "Interrupt (4 or 3)"); + +module_param(threshold, int, S_IRUGO); +MODULE_PARM_DESC(threshold, "space detection threshold (3)"); +#endif + +module_param(debug, bool, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Enable debugging messages"); diff --git a/drivers/staging/media/lirc/lirc_ttusbir.c b/drivers/staging/media/lirc/lirc_ttusbir.c new file mode 100644 index 000000000000..e4b329b8cafd --- /dev/null +++ b/drivers/staging/media/lirc/lirc_ttusbir.c @@ -0,0 +1,395 @@ +/* + * lirc_ttusbir.c + * + * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver + * + * Copyright (C) 2007 Stefan Macher + * + * This LIRC driver provides access to the TechnoTrend USB IR Receiver. + * The receiver delivers the IR signal as raw sampled true/false data in + * isochronous USB packets each of size 128 byte. + * Currently the driver reduces the sampling rate by factor of 8 as this + * is still more than enough to decode RC-5 - others should be analyzed. + * But the driver does not rely on RC-5 it should be able to decode every + * IR signal that is not too fast. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 + +MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC"); +MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)"); +MODULE_LICENSE("GPL"); + +/* #define DEBUG */ +#ifdef DEBUG +#define DPRINTK printk +#else +#define DPRINTK(_x_, a...) +#endif + +/* function declarations */ +static int probe(struct usb_interface *intf, const struct usb_device_id *id); +static void disconnect(struct usb_interface *intf); +static void urb_complete(struct urb *urb); +static int set_use_inc(void *data); +static void set_use_dec(void *data); + +static int num_urbs = 2; +module_param(num_urbs, int, S_IRUGO); +MODULE_PARM_DESC(num_urbs, + "Number of URBs in queue. Try to increase to 4 in case " + "of problems (default: 2; minimum: 2)"); + +/* table of devices that work with this driver */ +static struct usb_device_id device_id_table[] = { + /* TechnoTrend USB IR Receiver */ + { USB_DEVICE(0x0B48, 0x2003) }, + /* Terminating entry */ + { } +}; +MODULE_DEVICE_TABLE(usb, device_id_table); + +/* USB driver definition */ +static struct usb_driver usb_driver = { + .name = "TTUSBIR", + .id_table = &(device_id_table[0]), + .probe = probe, + .disconnect = disconnect, +}; + +/* USB device definition */ +struct ttusbir_device { + struct usb_driver *usb_driver; + struct usb_device *udev; + struct usb_interface *interf; + struct usb_class_driver class_driver; + unsigned int ifnum; /* Interface number to use */ + unsigned int alt_setting; /* alternate setting to use */ + unsigned int endpoint; /* Endpoint to use */ + struct urb **urb; /* num_urb URB pointers*/ + char **buffer; /* 128 byte buffer for each URB */ + struct lirc_buffer rbuf; /* Buffer towards LIRC */ + struct lirc_driver driver; + int minor; + int last_pulse; /* remembers if last received byte was pulse or space */ + int last_num; /* remembers how many last bytes appeared */ + int opened; +}; + +/*** LIRC specific functions ***/ +static int set_use_inc(void *data) +{ + int i, retval; + struct ttusbir_device *ttusbir = data; + + DPRINTK("Sending first URBs\n"); + /* @TODO Do I need to check if I am already opened */ + ttusbir->opened = 1; + + for (i = 0; i < num_urbs; i++) { + retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL); + if (retval) { + err("%s: usb_submit_urb failed on urb %d", + __func__, i); + return retval; + } + } + return 0; +} + +static void set_use_dec(void *data) +{ + struct ttusbir_device *ttusbir = data; + + DPRINTK("Device closed\n"); + + ttusbir->opened = 0; +} + +/*** USB specific functions ***/ + +/* + * This mapping table is used to do a very simple filtering of the + * input signal. + * For a value with at least 4 bits set it returns 0xFF otherwise + * 0x00. For faster IR signals this can not be used. But for RC-5 we + * still have about 14 samples per pulse/space, i.e. we sample with 14 + * times higher frequency than the signal frequency + */ +const unsigned char map_table[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 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, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF +}; + +static void urb_complete(struct urb *urb) +{ + struct ttusbir_device *ttusbir; + unsigned char *buf; + int i; + int l; + + ttusbir = urb->context; + + if (!ttusbir->opened) + return; + + buf = (unsigned char *)urb->transfer_buffer; + + for (i = 0; i < 128; i++) { + /* Here we do the filtering and some kind of down sampling */ + buf[i] = ~map_table[buf[i]]; + if (ttusbir->last_pulse == buf[i]) { + if (ttusbir->last_num < PULSE_MASK/63) + ttusbir->last_num++; + /* + * else we are in a idle period and do not need to + * increment any longer + */ + } else { + l = ttusbir->last_num * 62; /* about 62 = us/byte */ + if (ttusbir->last_pulse) /* pulse or space? */ + l |= PULSE_BIT; + if (!lirc_buffer_full(&ttusbir->rbuf)) { + lirc_buffer_write(&ttusbir->rbuf, (void *)&l); + wake_up_interruptible(&ttusbir->rbuf.wait_poll); + } + ttusbir->last_num = 0; + ttusbir->last_pulse = buf[i]; + } + } + usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */ +} + +/* + * Called whenever the USB subsystem thinks we could be the right driver + * to handle this device + */ +static int probe(struct usb_interface *intf, const struct usb_device_id *id) +{ + int alt_set, endp; + int found = 0; + int i, j; + int struct_size; + struct usb_host_interface *host_interf; + struct usb_interface_descriptor *interf_desc; + struct usb_host_endpoint *host_endpoint; + struct ttusbir_device *ttusbir; + + DPRINTK("Module ttusbir probe\n"); + + /* To reduce memory fragmentation we use only one allocation */ + struct_size = sizeof(struct ttusbir_device) + + (sizeof(struct urb *) * num_urbs) + + (sizeof(char *) * num_urbs) + + (num_urbs * 128); + ttusbir = kzalloc(struct_size, GFP_KERNEL); + if (!ttusbir) + return -ENOMEM; + + ttusbir->urb = (struct urb **)((char *)ttusbir + + sizeof(struct ttusbir_device)); + ttusbir->buffer = (char **)((char *)ttusbir->urb + + (sizeof(struct urb *) * num_urbs)); + for (i = 0; i < num_urbs; i++) + ttusbir->buffer[i] = (char *)ttusbir->buffer + + (sizeof(char *)*num_urbs) + (i * 128); + + ttusbir->usb_driver = &usb_driver; + ttusbir->alt_setting = -1; + /* @TODO check if error can be returned */ + ttusbir->udev = usb_get_dev(interface_to_usbdev(intf)); + ttusbir->interf = intf; + ttusbir->last_pulse = 0x00; + ttusbir->last_num = 0; + + /* + * Now look for interface setting we can handle + * We are searching for the alt setting where end point + * 0x82 has max packet size 16 + */ + for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) { + host_interf = &intf->altsetting[alt_set]; + interf_desc = &host_interf->desc; + for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) { + host_endpoint = &host_interf->endpoint[endp]; + if ((host_endpoint->desc.bEndpointAddress == 0x82) && + (host_endpoint->desc.wMaxPacketSize == 0x10)) { + ttusbir->alt_setting = alt_set; + ttusbir->endpoint = endp; + found = 1; + break; + } + } + } + if (ttusbir->alt_setting != -1) + DPRINTK("alt setting: %d\n", ttusbir->alt_setting); + else { + err("Could not find alternate setting\n"); + kfree(ttusbir); + return -EINVAL; + } + + /* OK lets setup this interface setting */ + usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting); + + /* Store device info in interface structure */ + usb_set_intfdata(intf, ttusbir); + + /* Register as a LIRC driver */ + if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) { + err("Could not get memory for LIRC data buffer\n"); + usb_set_intfdata(intf, NULL); + kfree(ttusbir); + return -ENOMEM; + } + strcpy(ttusbir->driver.name, "TTUSBIR"); + ttusbir->driver.minor = -1; + ttusbir->driver.code_length = 1; + ttusbir->driver.sample_rate = 0; + ttusbir->driver.data = ttusbir; + ttusbir->driver.add_to_buf = NULL; + ttusbir->driver.rbuf = &ttusbir->rbuf; + ttusbir->driver.set_use_inc = set_use_inc; + ttusbir->driver.set_use_dec = set_use_dec; + ttusbir->driver.dev = &intf->dev; + ttusbir->driver.owner = THIS_MODULE; + ttusbir->driver.features = LIRC_CAN_REC_MODE2; + ttusbir->minor = lirc_register_driver(&ttusbir->driver); + if (ttusbir->minor < 0) { + err("Error registering as LIRC driver\n"); + usb_set_intfdata(intf, NULL); + lirc_buffer_free(&ttusbir->rbuf); + kfree(ttusbir); + return -EIO; + } + + /* Allocate and setup the URB that we will use to talk to the device */ + for (i = 0; i < num_urbs; i++) { + ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL); + if (!ttusbir->urb[i]) { + err("Could not allocate memory for the URB\n"); + for (j = i - 1; j >= 0; j--) + kfree(ttusbir->urb[j]); + lirc_buffer_free(&ttusbir->rbuf); + lirc_unregister_driver(ttusbir->minor); + kfree(ttusbir); + usb_set_intfdata(intf, NULL); + return -ENOMEM; + } + ttusbir->urb[i]->dev = ttusbir->udev; + ttusbir->urb[i]->context = ttusbir; + ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev, + ttusbir->endpoint); + ttusbir->urb[i]->interval = 1; + ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP; + ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0]; + ttusbir->urb[i]->complete = urb_complete; + ttusbir->urb[i]->number_of_packets = 8; + ttusbir->urb[i]->transfer_buffer_length = 128; + for (j = 0; j < 8; j++) { + ttusbir->urb[i]->iso_frame_desc[j].offset = j*16; + ttusbir->urb[i]->iso_frame_desc[j].length = 16; + } + } + return 0; +} + +/** + * Called when the driver is unloaded or the device is unplugged + */ +static void disconnect(struct usb_interface *intf) +{ + int i; + struct ttusbir_device *ttusbir; + + DPRINTK("Module ttusbir disconnect\n"); + + ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf); + usb_set_intfdata(intf, NULL); + lirc_unregister_driver(ttusbir->minor); + DPRINTK("unregistered\n"); + + for (i = 0; i < num_urbs; i++) { + usb_kill_urb(ttusbir->urb[i]); + usb_free_urb(ttusbir->urb[i]); + } + DPRINTK("URBs killed\n"); + lirc_buffer_free(&ttusbir->rbuf); + kfree(ttusbir); +} + +static int ttusbir_init_module(void) +{ + int result; + + DPRINTK(KERN_DEBUG "Module ttusbir init\n"); + + /* register this driver with the USB subsystem */ + result = usb_register(&usb_driver); + if (result) + err("usb_register failed. Error number %d", result); + return result; +} + +static void ttusbir_exit_module(void) +{ + printk(KERN_DEBUG "Module ttusbir exit\n"); + usb_deregister(&usb_driver); +} + +module_init(ttusbir_init_module); +module_exit(ttusbir_exit_module); diff --git a/drivers/staging/media/lirc/lirc_zilog.c b/drivers/staging/media/lirc/lirc_zilog.c new file mode 100644 index 000000000000..0302d82a12f7 --- /dev/null +++ b/drivers/staging/media/lirc/lirc_zilog.c @@ -0,0 +1,1676 @@ +/* + * i2c IR lirc driver for devices with zilog IR processors + * + * Copyright (c) 2000 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 Asus TV-Box and Creative/VisionTek BreakOut-Box by + * Stefan Jahn + * modified for inclusion into kernel sources by + * Jerome Brock + * modified for Leadtek Winfast PVR2000 by + * Thomas Reitmayr (treitmayr@yahoo.com) + * modified for Hauppauge PVR-150 IR TX device by + * Mark Weaver + * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150 + * Jarod Wilson + * + * parts are cut&pasted from the lirc_i2c.c driver + * + * Numerous changes updating lirc_zilog.c in kernel 2.6.38 and later are + * Copyright (C) 2011 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +struct IR; + +struct IR_rx { + struct kref ref; + struct IR *ir; + + /* RX device */ + struct mutex client_lock; + struct i2c_client *c; + + /* RX polling thread data */ + struct task_struct *task; + + /* RX read data */ + unsigned char b[3]; + bool hdpvr_data_fmt; +}; + +struct IR_tx { + struct kref ref; + struct IR *ir; + + /* TX device */ + struct mutex client_lock; + struct i2c_client *c; + + /* TX additional actions needed */ + int need_boot; + bool post_tx_ready_poll; +}; + +struct IR { + struct kref ref; + struct list_head list; + + /* FIXME spinlock access to l.features */ + struct lirc_driver l; + struct lirc_buffer rbuf; + + struct mutex ir_lock; + atomic_t open_count; + + struct i2c_adapter *adapter; + + spinlock_t rx_ref_lock; /* struct IR_rx kref get()/put() */ + struct IR_rx *rx; + + spinlock_t tx_ref_lock; /* struct IR_tx kref get()/put() */ + struct IR_tx *tx; +}; + +/* IR transceiver instance object list */ +/* + * This lock is used for the following: + * a. ir_devices_list access, insertions, deletions + * b. struct IR kref get()s and put()s + * c. serialization of ir_probe() for the two i2c_clients for a Z8 + */ +static DEFINE_MUTEX(ir_devices_lock); +static LIST_HEAD(ir_devices_list); + +/* Block size for IR transmitter */ +#define TX_BLOCK_SIZE 99 + +/* Hauppauge IR transmitter data */ +struct tx_data_struct { + /* Boot block */ + unsigned char *boot_data; + + /* Start of binary data block */ + unsigned char *datap; + + /* End of binary data block */ + unsigned char *endp; + + /* Number of installed codesets */ + unsigned int num_code_sets; + + /* Pointers to codesets */ + unsigned char **code_sets; + + /* Global fixed data template */ + int fixed[TX_BLOCK_SIZE]; +}; + +static struct tx_data_struct *tx_data; +static struct mutex tx_data_lock; + +#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \ + ## args) +#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args) +#define zilog_info(s, args...) printk(KERN_INFO KBUILD_MODNAME ": " s, ## args) + +/* module parameters */ +static int debug; /* debug output */ +static int tx_only; /* only handle the IR Tx function */ +static int minor = -1; /* minor number */ + +#define dprintk(fmt, args...) \ + do { \ + if (debug) \ + printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \ + ## args); \ + } while (0) + + +/* struct IR reference counting */ +static struct IR *get_ir_device(struct IR *ir, bool ir_devices_lock_held) +{ + if (ir_devices_lock_held) { + kref_get(&ir->ref); + } else { + mutex_lock(&ir_devices_lock); + kref_get(&ir->ref); + mutex_unlock(&ir_devices_lock); + } + return ir; +} + +static void release_ir_device(struct kref *ref) +{ + struct IR *ir = container_of(ref, struct IR, ref); + + /* + * Things should be in this state by now: + * ir->rx set to NULL and deallocated - happens before ir->rx->ir put() + * ir->rx->task kthread stopped - happens before ir->rx->ir put() + * ir->tx set to NULL and deallocated - happens before ir->tx->ir put() + * ir->open_count == 0 - happens on final close() + * ir_lock, tx_ref_lock, rx_ref_lock, all released + */ + if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) { + lirc_unregister_driver(ir->l.minor); + ir->l.minor = MAX_IRCTL_DEVICES; + } + if (ir->rbuf.fifo_initialized) + lirc_buffer_free(&ir->rbuf); + list_del(&ir->list); + kfree(ir); +} + +static int put_ir_device(struct IR *ir, bool ir_devices_lock_held) +{ + int released; + + if (ir_devices_lock_held) + return kref_put(&ir->ref, release_ir_device); + + mutex_lock(&ir_devices_lock); + released = kref_put(&ir->ref, release_ir_device); + mutex_unlock(&ir_devices_lock); + + return released; +} + +/* struct IR_rx reference counting */ +static struct IR_rx *get_ir_rx(struct IR *ir) +{ + struct IR_rx *rx; + + spin_lock(&ir->rx_ref_lock); + rx = ir->rx; + if (rx != NULL) + kref_get(&rx->ref); + spin_unlock(&ir->rx_ref_lock); + return rx; +} + +static void destroy_rx_kthread(struct IR_rx *rx, bool ir_devices_lock_held) +{ + /* end up polling thread */ + if (!IS_ERR_OR_NULL(rx->task)) { + kthread_stop(rx->task); + rx->task = NULL; + /* Put the ir ptr that ir_probe() gave to the rx poll thread */ + put_ir_device(rx->ir, ir_devices_lock_held); + } +} + +static void release_ir_rx(struct kref *ref) +{ + struct IR_rx *rx = container_of(ref, struct IR_rx, ref); + struct IR *ir = rx->ir; + + /* + * This release function can't do all the work, as we want + * to keep the rx_ref_lock a spinlock, and killing the poll thread + * and releasing the ir reference can cause a sleep. That work is + * performed by put_ir_rx() + */ + ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; + /* Don't put_ir_device(rx->ir) here; lock can't be freed yet */ + ir->rx = NULL; + /* Don't do the kfree(rx) here; we still need to kill the poll thread */ + return; +} + +static int put_ir_rx(struct IR_rx *rx, bool ir_devices_lock_held) +{ + int released; + struct IR *ir = rx->ir; + + spin_lock(&ir->rx_ref_lock); + released = kref_put(&rx->ref, release_ir_rx); + spin_unlock(&ir->rx_ref_lock); + /* Destroy the rx kthread while not holding the spinlock */ + if (released) { + destroy_rx_kthread(rx, ir_devices_lock_held); + kfree(rx); + /* Make sure we're not still in a poll_table somewhere */ + wake_up_interruptible(&ir->rbuf.wait_poll); + } + /* Do a reference put() for the rx->ir reference, if we released rx */ + if (released) + put_ir_device(ir, ir_devices_lock_held); + return released; +} + +/* struct IR_tx reference counting */ +static struct IR_tx *get_ir_tx(struct IR *ir) +{ + struct IR_tx *tx; + + spin_lock(&ir->tx_ref_lock); + tx = ir->tx; + if (tx != NULL) + kref_get(&tx->ref); + spin_unlock(&ir->tx_ref_lock); + return tx; +} + +static void release_ir_tx(struct kref *ref) +{ + struct IR_tx *tx = container_of(ref, struct IR_tx, ref); + struct IR *ir = tx->ir; + + ir->l.features &= ~LIRC_CAN_SEND_PULSE; + /* Don't put_ir_device(tx->ir) here, so our lock doesn't get freed */ + ir->tx = NULL; + kfree(tx); +} + +static int put_ir_tx(struct IR_tx *tx, bool ir_devices_lock_held) +{ + int released; + struct IR *ir = tx->ir; + + spin_lock(&ir->tx_ref_lock); + released = kref_put(&tx->ref, release_ir_tx); + spin_unlock(&ir->tx_ref_lock); + /* Do a reference put() for the tx->ir reference, if we released tx */ + if (released) + put_ir_device(ir, ir_devices_lock_held); + return released; +} + +static int add_to_buf(struct IR *ir) +{ + __u16 code; + unsigned char codes[2]; + unsigned char keybuf[6]; + int got_data = 0; + int ret; + int failures = 0; + unsigned char sendbuf[1] = { 0 }; + struct lirc_buffer *rbuf = ir->l.rbuf; + struct IR_rx *rx; + struct IR_tx *tx; + + if (lirc_buffer_full(rbuf)) { + dprintk("buffer overflow\n"); + return -EOVERFLOW; + } + + rx = get_ir_rx(ir); + if (rx == NULL) + return -ENXIO; + + /* Ensure our rx->c i2c_client remains valid for the duration */ + mutex_lock(&rx->client_lock); + if (rx->c == NULL) { + mutex_unlock(&rx->client_lock); + put_ir_rx(rx, false); + return -ENXIO; + } + + tx = get_ir_tx(ir); + + /* + * service the device as long as it is returning + * data and we have space + */ + do { + if (kthread_should_stop()) { + ret = -ENODATA; + break; + } + + /* + * Lock i2c bus for the duration. RX/TX chips interfere so + * this is worth it + */ + mutex_lock(&ir->ir_lock); + + if (kthread_should_stop()) { + mutex_unlock(&ir->ir_lock); + ret = -ENODATA; + break; + } + + /* + * Send random "poll command" (?) Windows driver does this + * and it is a good point to detect chip failure. + */ + ret = i2c_master_send(rx->c, sendbuf, 1); + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + if (failures >= 3) { + mutex_unlock(&ir->ir_lock); + zilog_error("unable to read from the IR chip " + "after 3 resets, giving up\n"); + break; + } + + /* Looks like the chip crashed, reset it */ + zilog_error("polling the IR receiver chip failed, " + "trying reset\n"); + + set_current_state(TASK_UNINTERRUPTIBLE); + if (kthread_should_stop()) { + mutex_unlock(&ir->ir_lock); + ret = -ENODATA; + break; + } + schedule_timeout((100 * HZ + 999) / 1000); + if (tx != NULL) + tx->need_boot = 1; + + ++failures; + mutex_unlock(&ir->ir_lock); + ret = 0; + continue; + } + + if (kthread_should_stop()) { + mutex_unlock(&ir->ir_lock); + ret = -ENODATA; + break; + } + ret = i2c_master_recv(rx->c, keybuf, sizeof(keybuf)); + mutex_unlock(&ir->ir_lock); + if (ret != sizeof(keybuf)) { + zilog_error("i2c_master_recv failed with %d -- " + "keeping last read buffer\n", ret); + } else { + rx->b[0] = keybuf[3]; + rx->b[1] = keybuf[4]; + rx->b[2] = keybuf[5]; + dprintk("key (0x%02x/0x%02x)\n", rx->b[0], rx->b[1]); + } + + /* key pressed ? */ + if (rx->hdpvr_data_fmt) { + if (got_data && (keybuf[0] == 0x80)) { + ret = 0; + break; + } else if (got_data && (keybuf[0] == 0x00)) { + ret = -ENODATA; + break; + } + } else if ((rx->b[0] & 0x80) == 0) { + ret = got_data ? 0 : -ENODATA; + break; + } + + /* look what we have */ + code = (((__u16)rx->b[0] & 0x7f) << 6) | (rx->b[1] >> 2); + + codes[0] = (code >> 8) & 0xff; + codes[1] = code & 0xff; + + /* return it */ + lirc_buffer_write(rbuf, codes); + ++got_data; + ret = 0; + } while (!lirc_buffer_full(rbuf)); + + mutex_unlock(&rx->client_lock); + if (tx != NULL) + put_ir_tx(tx, false); + put_ir_rx(rx, false); + return ret; +} + +/* + * Main function of the polling thread -- from lirc_dev. + * We don't fit the LIRC model at all anymore. This is horrible, but + * basically we have a single RX/TX device with a nasty failure mode + * that needs to be accounted for across the pair. lirc lets us provide + * fops, but prevents us from using the internal polling, etc. if we do + * so. Hence the replication. Might be neater to extend the LIRC model + * to account for this but I'd think it's a very special case of seriously + * messed up hardware. + */ +static int lirc_thread(void *arg) +{ + struct IR *ir = arg; + struct lirc_buffer *rbuf = ir->l.rbuf; + + dprintk("poll thread started\n"); + + while (!kthread_should_stop()) { + set_current_state(TASK_INTERRUPTIBLE); + + /* if device not opened, we can sleep half a second */ + if (atomic_read(&ir->open_count) == 0) { + schedule_timeout(HZ/2); + continue; + } + + /* + * This is ~113*2 + 24 + jitter (2*repeat gap + code length). + * We use this interval as the chip resets every time you poll + * it (bad!). This is therefore just sufficient to catch all + * of the button presses. It makes the remote much more + * responsive. You can see the difference by running irw and + * holding down a button. With 100ms, the old polling + * interval, you'll notice breaks in the repeat sequence + * corresponding to lost keypresses. + */ + schedule_timeout((260 * HZ) / 1000); + if (kthread_should_stop()) + break; + if (!add_to_buf(ir)) + wake_up_interruptible(&rbuf->wait_poll); + } + + dprintk("poll thread ended\n"); + return 0; +} + +static int set_use_inc(void *data) +{ + return 0; +} + +static void set_use_dec(void *data) +{ + return; +} + +/* safe read of a uint32 (always network byte order) */ +static int read_uint32(unsigned char **data, + unsigned char *endp, unsigned int *val) +{ + if (*data + 4 > endp) + return 0; + *val = ((*data)[0] << 24) | ((*data)[1] << 16) | + ((*data)[2] << 8) | (*data)[3]; + *data += 4; + return 1; +} + +/* safe read of a uint8 */ +static int read_uint8(unsigned char **data, + unsigned char *endp, unsigned char *val) +{ + if (*data + 1 > endp) + return 0; + *val = *((*data)++); + return 1; +} + +/* safe skipping of N bytes */ +static int skip(unsigned char **data, + unsigned char *endp, unsigned int distance) +{ + if (*data + distance > endp) + return 0; + *data += distance; + return 1; +} + +/* decompress key data into the given buffer */ +static int get_key_data(unsigned char *buf, + unsigned int codeset, unsigned int key) +{ + unsigned char *data, *endp, *diffs, *key_block; + unsigned char keys, ndiffs, id; + unsigned int base, lim, pos, i; + + /* Binary search for the codeset */ + for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) { + pos = base + (lim >> 1); + data = tx_data->code_sets[pos]; + + if (!read_uint32(&data, tx_data->endp, &i)) + goto corrupt; + + if (i == codeset) + break; + else if (codeset > i) { + base = pos + 1; + --lim; + } + } + /* Not found? */ + if (!lim) + return -EPROTO; + + /* Set end of data block */ + endp = pos < tx_data->num_code_sets - 1 ? + tx_data->code_sets[pos + 1] : tx_data->endp; + + /* Read the block header */ + if (!read_uint8(&data, endp, &keys) || + !read_uint8(&data, endp, &ndiffs) || + ndiffs > TX_BLOCK_SIZE || keys == 0) + goto corrupt; + + /* Save diffs & skip */ + diffs = data; + if (!skip(&data, endp, ndiffs)) + goto corrupt; + + /* Read the id of the first key */ + if (!read_uint8(&data, endp, &id)) + goto corrupt; + + /* Unpack the first key's data */ + for (i = 0; i < TX_BLOCK_SIZE; ++i) { + if (tx_data->fixed[i] == -1) { + if (!read_uint8(&data, endp, &buf[i])) + goto corrupt; + } else { + buf[i] = (unsigned char)tx_data->fixed[i]; + } + } + + /* Early out key found/not found */ + if (key == id) + return 0; + if (keys == 1) + return -EPROTO; + + /* Sanity check */ + key_block = data; + if (!skip(&data, endp, (keys - 1) * (ndiffs + 1))) + goto corrupt; + + /* Binary search for the key */ + for (base = 0, lim = keys - 1; lim; lim >>= 1) { + /* Seek to block */ + unsigned char *key_data; + pos = base + (lim >> 1); + key_data = key_block + (ndiffs + 1) * pos; + + if (*key_data == key) { + /* skip key id */ + ++key_data; + + /* found, so unpack the diffs */ + for (i = 0; i < ndiffs; ++i) { + unsigned char val; + if (!read_uint8(&key_data, endp, &val) || + diffs[i] >= TX_BLOCK_SIZE) + goto corrupt; + buf[diffs[i]] = val; + } + + return 0; + } else if (key > *key_data) { + base = pos + 1; + --lim; + } + } + /* Key not found */ + return -EPROTO; + +corrupt: + zilog_error("firmware is corrupt\n"); + return -EFAULT; +} + +/* send a block of data to the IR TX device */ +static int send_data_block(struct IR_tx *tx, unsigned char *data_block) +{ + int i, j, ret; + unsigned char buf[5]; + + for (i = 0; i < TX_BLOCK_SIZE;) { + int tosend = TX_BLOCK_SIZE - i; + if (tosend > 4) + tosend = 4; + buf[0] = (unsigned char)(i + 1); + for (j = 0; j < tosend; ++j) + buf[1 + j] = data_block[i + j]; + dprintk("%02x %02x %02x %02x %02x", + buf[0], buf[1], buf[2], buf[3], buf[4]); + ret = i2c_master_send(tx->c, buf, tosend + 1); + if (ret != tosend + 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + i += tosend; + } + return 0; +} + +/* send boot data to the IR TX device */ +static int send_boot_data(struct IR_tx *tx) +{ + int ret, i; + unsigned char buf[4]; + + /* send the boot block */ + ret = send_data_block(tx, tx_data->boot_data); + if (ret != 0) + return ret; + + /* Hit the go button to activate the new boot data */ + buf[0] = 0x00; + buf[1] = 0x20; + ret = i2c_master_send(tx->c, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* + * Wait for zilog to settle after hitting go post boot block upload. + * Without this delay, the HD-PVR and HVR-1950 both return an -EIO + * upon attempting to get firmware revision, and tx probe thus fails. + */ + for (i = 0; i < 10; i++) { + ret = i2c_master_send(tx->c, buf, 1); + if (ret == 1) + break; + udelay(100); + } + + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Here comes the firmware version... (hopefully) */ + ret = i2c_master_recv(tx->c, buf, 4); + if (ret != 4) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return 0; + } + if ((buf[0] != 0x80) && (buf[0] != 0xa0)) { + zilog_error("unexpected IR TX init response: %02x\n", buf[0]); + return 0; + } + zilog_notify("Zilog/Hauppauge IR blaster firmware version " + "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]); + + return 0; +} + +/* unload "firmware", lock held */ +static void fw_unload_locked(void) +{ + if (tx_data) { + if (tx_data->code_sets) + vfree(tx_data->code_sets); + + if (tx_data->datap) + vfree(tx_data->datap); + + vfree(tx_data); + tx_data = NULL; + dprintk("successfully unloaded IR blaster firmware\n"); + } +} + +/* unload "firmware" for the IR TX device */ +static void fw_unload(void) +{ + mutex_lock(&tx_data_lock); + fw_unload_locked(); + mutex_unlock(&tx_data_lock); +} + +/* load "firmware" for the IR TX device */ +static int fw_load(struct IR_tx *tx) +{ + int ret; + unsigned int i; + unsigned char *data, version, num_global_fixed; + const struct firmware *fw_entry; + + /* Already loaded? */ + mutex_lock(&tx_data_lock); + if (tx_data) { + ret = 0; + goto out; + } + + /* Request codeset data file */ + ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", tx->ir->l.dev); + if (ret != 0) { + zilog_error("firmware haup-ir-blaster.bin not available " + "(%d)\n", ret); + ret = ret < 0 ? ret : -EFAULT; + goto out; + } + dprintk("firmware of size %zu loaded\n", fw_entry->size); + + /* Parse the file */ + tx_data = vmalloc(sizeof(*tx_data)); + if (tx_data == NULL) { + zilog_error("out of memory\n"); + release_firmware(fw_entry); + ret = -ENOMEM; + goto out; + } + tx_data->code_sets = NULL; + + /* Copy the data so hotplug doesn't get confused and timeout */ + tx_data->datap = vmalloc(fw_entry->size); + if (tx_data->datap == NULL) { + zilog_error("out of memory\n"); + release_firmware(fw_entry); + vfree(tx_data); + ret = -ENOMEM; + goto out; + } + memcpy(tx_data->datap, fw_entry->data, fw_entry->size); + tx_data->endp = tx_data->datap + fw_entry->size; + release_firmware(fw_entry); fw_entry = NULL; + + /* Check version */ + data = tx_data->datap; + if (!read_uint8(&data, tx_data->endp, &version)) + goto corrupt; + if (version != 1) { + zilog_error("unsupported code set file version (%u, expected" + "1) -- please upgrade to a newer driver", + version); + fw_unload_locked(); + ret = -EFAULT; + goto out; + } + + /* Save boot block for later */ + tx_data->boot_data = data; + if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE)) + goto corrupt; + + if (!read_uint32(&data, tx_data->endp, + &tx_data->num_code_sets)) + goto corrupt; + + dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets); + + tx_data->code_sets = vmalloc( + tx_data->num_code_sets * sizeof(char *)); + if (tx_data->code_sets == NULL) { + fw_unload_locked(); + ret = -ENOMEM; + goto out; + } + + for (i = 0; i < TX_BLOCK_SIZE; ++i) + tx_data->fixed[i] = -1; + + /* Read global fixed data template */ + if (!read_uint8(&data, tx_data->endp, &num_global_fixed) || + num_global_fixed > TX_BLOCK_SIZE) + goto corrupt; + for (i = 0; i < num_global_fixed; ++i) { + unsigned char pos, val; + if (!read_uint8(&data, tx_data->endp, &pos) || + !read_uint8(&data, tx_data->endp, &val) || + pos >= TX_BLOCK_SIZE) + goto corrupt; + tx_data->fixed[pos] = (int)val; + } + + /* Filch out the position of each code set */ + for (i = 0; i < tx_data->num_code_sets; ++i) { + unsigned int id; + unsigned char keys; + unsigned char ndiffs; + + /* Save the codeset position */ + tx_data->code_sets[i] = data; + + /* Read header */ + if (!read_uint32(&data, tx_data->endp, &id) || + !read_uint8(&data, tx_data->endp, &keys) || + !read_uint8(&data, tx_data->endp, &ndiffs) || + ndiffs > TX_BLOCK_SIZE || keys == 0) + goto corrupt; + + /* skip diff positions */ + if (!skip(&data, tx_data->endp, ndiffs)) + goto corrupt; + + /* + * After the diffs we have the first key id + data - + * global fixed + */ + if (!skip(&data, tx_data->endp, + 1 + TX_BLOCK_SIZE - num_global_fixed)) + goto corrupt; + + /* Then we have keys-1 blocks of key id+diffs */ + if (!skip(&data, tx_data->endp, + (ndiffs + 1) * (keys - 1))) + goto corrupt; + } + ret = 0; + goto out; + +corrupt: + zilog_error("firmware is corrupt\n"); + fw_unload_locked(); + ret = -EFAULT; + +out: + mutex_unlock(&tx_data_lock); + return ret; +} + +/* copied from lirc_dev */ +static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos) +{ + struct IR *ir = filep->private_data; + struct IR_rx *rx; + struct lirc_buffer *rbuf = ir->l.rbuf; + int ret = 0, written = 0, retries = 0; + unsigned int m; + DECLARE_WAITQUEUE(wait, current); + + dprintk("read called\n"); + if (n % rbuf->chunk_size) { + dprintk("read result = -EINVAL\n"); + return -EINVAL; + } + + rx = get_ir_rx(ir); + if (rx == NULL) + return -ENXIO; + + /* + * we add ourselves to the task queue before buffer check + * to avoid losing scan code (in case when queue is awaken somewhere + * between while condition checking and scheduling) + */ + add_wait_queue(&rbuf->wait_poll, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + /* + * while we didn't provide 'length' bytes, device is opened in blocking + * mode and 'copy_to_user' is happy, wait for data. + */ + while (written < n && ret == 0) { + if (lirc_buffer_empty(rbuf)) { + /* + * According to the read(2) man page, 'written' can be + * returned as less than 'n', instead of blocking + * again, returning -EWOULDBLOCK, or returning + * -ERESTARTSYS + */ + if (written) + break; + if (filep->f_flags & O_NONBLOCK) { + ret = -EWOULDBLOCK; + break; + } + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } else { + unsigned char buf[rbuf->chunk_size]; + m = lirc_buffer_read(rbuf, buf); + if (m == rbuf->chunk_size) { + ret = copy_to_user((void *)outbuf+written, buf, + rbuf->chunk_size); + written += rbuf->chunk_size; + } else { + retries++; + } + if (retries >= 5) { + zilog_error("Buffer read failed!\n"); + ret = -EIO; + } + } + } + + remove_wait_queue(&rbuf->wait_poll, &wait); + put_ir_rx(rx, false); + set_current_state(TASK_RUNNING); + + dprintk("read result = %d (%s)\n", ret, ret ? "Error" : "OK"); + + return ret ? ret : written; +} + +/* send a keypress to the IR TX device */ +static int send_code(struct IR_tx *tx, unsigned int code, unsigned int key) +{ + unsigned char data_block[TX_BLOCK_SIZE]; + unsigned char buf[2]; + int i, ret; + + /* Get data for the codeset/key */ + ret = get_key_data(data_block, code, key); + + if (ret == -EPROTO) { + zilog_error("failed to get data for code %u, key %u -- check " + "lircd.conf entries\n", code, key); + return ret; + } else if (ret != 0) + return ret; + + /* Send the data block */ + ret = send_data_block(tx, data_block); + if (ret != 0) + return ret; + + /* Send data block length? */ + buf[0] = 0x00; + buf[1] = 0x40; + ret = i2c_master_send(tx->c, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Give the z8 a moment to process data block */ + for (i = 0; i < 10; i++) { + ret = i2c_master_send(tx->c, buf, 1); + if (ret == 1) + break; + udelay(100); + } + + if (ret != 1) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Send finished download? */ + ret = i2c_master_recv(tx->c, buf, 1); + if (ret != 1) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + if (buf[0] != 0xA0) { + zilog_error("unexpected IR TX response #1: %02x\n", + buf[0]); + return -EFAULT; + } + + /* Send prepare command? */ + buf[0] = 0x00; + buf[1] = 0x80; + ret = i2c_master_send(tx->c, buf, 2); + if (ret != 2) { + zilog_error("i2c_master_send failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* + * The sleep bits aren't necessary on the HD PVR, and in fact, the + * last i2c_master_recv always fails with a -5, so for now, we're + * going to skip this whole mess and say we're done on the HD PVR + */ + if (!tx->post_tx_ready_poll) { + dprintk("sent code %u, key %u\n", code, key); + return 0; + } + + /* + * This bit NAKs until the device is ready, so we retry it + * sleeping a bit each time. This seems to be what the windows + * driver does, approximately. + * Try for up to 1s. + */ + for (i = 0; i < 20; ++i) { + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((50 * HZ + 999) / 1000); + ret = i2c_master_send(tx->c, buf, 1); + if (ret == 1) + break; + dprintk("NAK expected: i2c_master_send " + "failed with %d (try %d)\n", ret, i+1); + } + if (ret != 1) { + zilog_error("IR TX chip never got ready: last i2c_master_send " + "failed with %d\n", ret); + return ret < 0 ? ret : -EFAULT; + } + + /* Seems to be an 'ok' response */ + i = i2c_master_recv(tx->c, buf, 1); + if (i != 1) { + zilog_error("i2c_master_recv failed with %d\n", ret); + return -EFAULT; + } + if (buf[0] != 0x80) { + zilog_error("unexpected IR TX response #2: %02x\n", buf[0]); + return -EFAULT; + } + + /* Oh good, it worked */ + dprintk("sent code %u, key %u\n", code, key); + return 0; +} + +/* + * Write a code to the device. We take in a 32-bit number (an int) and then + * decode this to a codeset/key index. The key data is then decompressed and + * sent to the device. We have a spin lock as per i2c documentation to prevent + * multiple concurrent sends which would probably cause the device to explode. + */ +static ssize_t write(struct file *filep, const char *buf, size_t n, + loff_t *ppos) +{ + struct IR *ir = filep->private_data; + struct IR_tx *tx; + size_t i; + int failures = 0; + + /* Validate user parameters */ + if (n % sizeof(int)) + return -EINVAL; + + /* Get a struct IR_tx reference */ + tx = get_ir_tx(ir); + if (tx == NULL) + return -ENXIO; + + /* Ensure our tx->c i2c_client remains valid for the duration */ + mutex_lock(&tx->client_lock); + if (tx->c == NULL) { + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + return -ENXIO; + } + + /* Lock i2c bus for the duration */ + mutex_lock(&ir->ir_lock); + + /* Send each keypress */ + for (i = 0; i < n;) { + int ret = 0; + int command; + + if (copy_from_user(&command, buf + i, sizeof(command))) { + mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + return -EFAULT; + } + + /* Send boot data first if required */ + if (tx->need_boot == 1) { + /* Make sure we have the 'firmware' loaded, first */ + ret = fw_load(tx); + if (ret != 0) { + mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + if (ret != -ENOMEM) + ret = -EIO; + return ret; + } + /* Prep the chip for transmitting codes */ + ret = send_boot_data(tx); + if (ret == 0) + tx->need_boot = 0; + } + + /* Send the code */ + if (ret == 0) { + ret = send_code(tx, (unsigned)command >> 16, + (unsigned)command & 0xFFFF); + if (ret == -EPROTO) { + mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + return ret; + } + } + + /* + * Hmm, a failure. If we've had a few then give up, otherwise + * try a reset + */ + if (ret != 0) { + /* Looks like the chip crashed, reset it */ + zilog_error("sending to the IR transmitter chip " + "failed, trying reset\n"); + + if (failures >= 3) { + zilog_error("unable to send to the IR chip " + "after 3 resets, giving up\n"); + mutex_unlock(&ir->ir_lock); + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + return ret; + } + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((100 * HZ + 999) / 1000); + tx->need_boot = 1; + ++failures; + } else + i += sizeof(int); + } + + /* Release i2c bus */ + mutex_unlock(&ir->ir_lock); + + mutex_unlock(&tx->client_lock); + + /* Give back our struct IR_tx reference */ + put_ir_tx(tx, false); + + /* All looks good */ + return n; +} + +/* copied from lirc_dev */ +static unsigned int poll(struct file *filep, poll_table *wait) +{ + struct IR *ir = filep->private_data; + struct IR_rx *rx; + struct lirc_buffer *rbuf = ir->l.rbuf; + unsigned int ret; + + dprintk("poll called\n"); + + rx = get_ir_rx(ir); + if (rx == NULL) { + /* + * Revisit this, if our poll function ever reports writeable + * status for Tx + */ + dprintk("poll result = POLLERR\n"); + return POLLERR; + } + + /* + * Add our lirc_buffer's wait_queue to the poll_table. A wake up on + * that buffer's wait queue indicates we may have a new poll status. + */ + poll_wait(filep, &rbuf->wait_poll, wait); + + /* Indicate what ops could happen immediately without blocking */ + ret = lirc_buffer_empty(rbuf) ? 0 : (POLLIN|POLLRDNORM); + + dprintk("poll result = %s\n", ret ? "POLLIN|POLLRDNORM" : "none"); + return ret; +} + +static long ioctl(struct file *filep, unsigned int cmd, unsigned long arg) +{ + struct IR *ir = filep->private_data; + int result; + unsigned long mode, features; + + features = ir->l.features; + + switch (cmd) { + case LIRC_GET_LENGTH: + result = put_user((unsigned long)13, + (unsigned long *)arg); + break; + case LIRC_GET_FEATURES: + result = put_user(features, (unsigned long *) arg); + break; + case LIRC_GET_REC_MODE: + if (!(features&LIRC_CAN_REC_MASK)) + return -ENOSYS; + + result = put_user(LIRC_REC2MODE + (features&LIRC_CAN_REC_MASK), + (unsigned long *)arg); + break; + case LIRC_SET_REC_MODE: + if (!(features&LIRC_CAN_REC_MASK)) + return -ENOSYS; + + result = get_user(mode, (unsigned long *)arg); + if (!result && !(LIRC_MODE2REC(mode) & features)) + result = -EINVAL; + break; + case LIRC_GET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + + result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg); + break; + case LIRC_SET_SEND_MODE: + if (!(features&LIRC_CAN_SEND_MASK)) + return -ENOSYS; + + result = get_user(mode, (unsigned long *) arg); + if (!result && mode != LIRC_MODE_PULSE) + return -EINVAL; + break; + default: + return -EINVAL; + } + return result; +} + +static struct IR *get_ir_device_by_minor(unsigned int minor) +{ + struct IR *ir; + struct IR *ret = NULL; + + mutex_lock(&ir_devices_lock); + + if (!list_empty(&ir_devices_list)) { + list_for_each_entry(ir, &ir_devices_list, list) { + if (ir->l.minor == minor) { + ret = get_ir_device(ir, true); + break; + } + } + } + + mutex_unlock(&ir_devices_lock); + return ret; +} + +/* + * Open the IR device. Get hold of our IR structure and + * stash it in private_data for the file + */ +static int open(struct inode *node, struct file *filep) +{ + struct IR *ir; + unsigned int minor = MINOR(node->i_rdev); + + /* find our IR struct */ + ir = get_ir_device_by_minor(minor); + + if (ir == NULL) + return -ENODEV; + + atomic_inc(&ir->open_count); + + /* stash our IR struct */ + filep->private_data = ir; + + nonseekable_open(node, filep); + return 0; +} + +/* Close the IR device */ +static int close(struct inode *node, struct file *filep) +{ + /* find our IR struct */ + struct IR *ir = filep->private_data; + if (ir == NULL) { + zilog_error("close: no private_data attached to the file!\n"); + return -ENODEV; + } + + atomic_dec(&ir->open_count); + + put_ir_device(ir, false); + return 0; +} + +static int ir_remove(struct i2c_client *client); +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id); + +#define ID_FLAG_TX 0x01 +#define ID_FLAG_HDPVR 0x02 + +static const struct i2c_device_id ir_transceiver_id[] = { + { "ir_tx_z8f0811_haup", ID_FLAG_TX }, + { "ir_rx_z8f0811_haup", 0 }, + { "ir_tx_z8f0811_hdpvr", ID_FLAG_HDPVR | ID_FLAG_TX }, + { "ir_rx_z8f0811_hdpvr", ID_FLAG_HDPVR }, + { } +}; + +static struct i2c_driver driver = { + .driver = { + .owner = THIS_MODULE, + .name = "Zilog/Hauppauge i2c IR", + }, + .probe = ir_probe, + .remove = ir_remove, + .id_table = ir_transceiver_id, +}; + +static const struct file_operations lirc_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = read, + .write = write, + .poll = poll, + .unlocked_ioctl = ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = ioctl, +#endif + .open = open, + .release = close +}; + +static struct lirc_driver lirc_template = { + .name = "lirc_zilog", + .minor = -1, + .code_length = 13, + .buffer_size = BUFLEN / 2, + .sample_rate = 0, /* tell lirc_dev to not start its own kthread */ + .chunk_size = 2, + .set_use_inc = set_use_inc, + .set_use_dec = set_use_dec, + .fops = &lirc_fops, + .owner = THIS_MODULE, +}; + +static int ir_remove(struct i2c_client *client) +{ + if (strncmp("ir_tx_z8", client->name, 8) == 0) { + struct IR_tx *tx = i2c_get_clientdata(client); + if (tx != NULL) { + mutex_lock(&tx->client_lock); + tx->c = NULL; + mutex_unlock(&tx->client_lock); + put_ir_tx(tx, false); + } + } else if (strncmp("ir_rx_z8", client->name, 8) == 0) { + struct IR_rx *rx = i2c_get_clientdata(client); + if (rx != NULL) { + mutex_lock(&rx->client_lock); + rx->c = NULL; + mutex_unlock(&rx->client_lock); + put_ir_rx(rx, false); + } + } + return 0; +} + + +/* ir_devices_lock must be held */ +static struct IR *get_ir_device_by_adapter(struct i2c_adapter *adapter) +{ + struct IR *ir; + + if (list_empty(&ir_devices_list)) + return NULL; + + list_for_each_entry(ir, &ir_devices_list, list) + if (ir->adapter == adapter) { + get_ir_device(ir, true); + return ir; + } + + return NULL; +} + +static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct IR *ir; + struct IR_tx *tx; + struct IR_rx *rx; + struct i2c_adapter *adap = client->adapter; + int ret; + bool tx_probe = false; + + dprintk("%s: %s on i2c-%d (%s), client addr=0x%02x\n", + __func__, id->name, adap->nr, adap->name, client->addr); + + /* + * The IR receiver is at i2c address 0x71. + * The IR transmitter is at i2c address 0x70. + */ + + if (id->driver_data & ID_FLAG_TX) + tx_probe = true; + else if (tx_only) /* module option */ + return -ENXIO; + + zilog_info("probing IR %s on %s (i2c-%d)\n", + tx_probe ? "Tx" : "Rx", adap->name, adap->nr); + + mutex_lock(&ir_devices_lock); + + /* Use a single struct IR instance for both the Rx and Tx functions */ + ir = get_ir_device_by_adapter(adap); + if (ir == NULL) { + ir = kzalloc(sizeof(struct IR), GFP_KERNEL); + if (ir == NULL) { + ret = -ENOMEM; + goto out_no_ir; + } + kref_init(&ir->ref); + + /* store for use in ir_probe() again, and open() later on */ + INIT_LIST_HEAD(&ir->list); + list_add_tail(&ir->list, &ir_devices_list); + + ir->adapter = adap; + mutex_init(&ir->ir_lock); + atomic_set(&ir->open_count, 0); + spin_lock_init(&ir->tx_ref_lock); + spin_lock_init(&ir->rx_ref_lock); + + /* set lirc_dev stuff */ + memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver)); + /* + * FIXME this is a pointer reference to us, but no refcount. + * + * This OK for now, since lirc_dev currently won't touch this + * buffer as we provide our own lirc_fops. + * + * Currently our own lirc_fops rely on this ir->l.rbuf pointer + */ + ir->l.rbuf = &ir->rbuf; + ir->l.dev = &adap->dev; + ret = lirc_buffer_init(ir->l.rbuf, + ir->l.chunk_size, ir->l.buffer_size); + if (ret) + goto out_put_ir; + } + + if (tx_probe) { + /* Get the IR_rx instance for later, if already allocated */ + rx = get_ir_rx(ir); + + /* Set up a struct IR_tx instance */ + tx = kzalloc(sizeof(struct IR_tx), GFP_KERNEL); + if (tx == NULL) { + ret = -ENOMEM; + goto out_put_xx; + } + kref_init(&tx->ref); + ir->tx = tx; + + ir->l.features |= LIRC_CAN_SEND_PULSE; + mutex_init(&tx->client_lock); + tx->c = client; + tx->need_boot = 1; + tx->post_tx_ready_poll = + (id->driver_data & ID_FLAG_HDPVR) ? false : true; + + /* An ir ref goes to the struct IR_tx instance */ + tx->ir = get_ir_device(ir, true); + + /* A tx ref goes to the i2c_client */ + i2c_set_clientdata(client, get_ir_tx(ir)); + + /* + * Load the 'firmware'. We do this before registering with + * lirc_dev, so the first firmware load attempt does not happen + * after a open() or write() call on the device. + * + * Failure here is not deemed catastrophic, so the receiver will + * still be usable. Firmware load will be retried in write(), + * if it is needed. + */ + fw_load(tx); + + /* Proceed only if the Rx client is also ready or not needed */ + if (rx == NULL && !tx_only) { + zilog_info("probe of IR Tx on %s (i2c-%d) done. Waiting" + " on IR Rx.\n", adap->name, adap->nr); + goto out_ok; + } + } else { + /* Get the IR_tx instance for later, if already allocated */ + tx = get_ir_tx(ir); + + /* Set up a struct IR_rx instance */ + rx = kzalloc(sizeof(struct IR_rx), GFP_KERNEL); + if (rx == NULL) { + ret = -ENOMEM; + goto out_put_xx; + } + kref_init(&rx->ref); + ir->rx = rx; + + ir->l.features |= LIRC_CAN_REC_LIRCCODE; + mutex_init(&rx->client_lock); + rx->c = client; + rx->hdpvr_data_fmt = + (id->driver_data & ID_FLAG_HDPVR) ? true : false; + + /* An ir ref goes to the struct IR_rx instance */ + rx->ir = get_ir_device(ir, true); + + /* An rx ref goes to the i2c_client */ + i2c_set_clientdata(client, get_ir_rx(ir)); + + /* + * Start the polling thread. + * It will only perform an empty loop around schedule_timeout() + * until we register with lirc_dev and the first user open() + */ + /* An ir ref goes to the new rx polling kthread */ + rx->task = kthread_run(lirc_thread, get_ir_device(ir, true), + "zilog-rx-i2c-%d", adap->nr); + if (IS_ERR(rx->task)) { + ret = PTR_ERR(rx->task); + zilog_error("%s: could not start IR Rx polling thread" + "\n", __func__); + /* Failed kthread, so put back the ir ref */ + put_ir_device(ir, true); + /* Failure exit, so put back rx ref from i2c_client */ + i2c_set_clientdata(client, NULL); + put_ir_rx(rx, true); + ir->l.features &= ~LIRC_CAN_REC_LIRCCODE; + goto out_put_xx; + } + + /* Proceed only if the Tx client is also ready */ + if (tx == NULL) { + zilog_info("probe of IR Rx on %s (i2c-%d) done. Waiting" + " on IR Tx.\n", adap->name, adap->nr); + goto out_ok; + } + } + + /* register with lirc */ + ir->l.minor = minor; /* module option: user requested minor number */ + ir->l.minor = lirc_register_driver(&ir->l); + if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) { + zilog_error("%s: \"minor\" must be between 0 and %d (%d)!\n", + __func__, MAX_IRCTL_DEVICES-1, ir->l.minor); + ret = -EBADRQC; + goto out_put_xx; + } + zilog_info("IR unit on %s (i2c-%d) registered as lirc%d and ready\n", + adap->name, adap->nr, ir->l.minor); + +out_ok: + if (rx != NULL) + put_ir_rx(rx, true); + if (tx != NULL) + put_ir_tx(tx, true); + put_ir_device(ir, true); + zilog_info("probe of IR %s on %s (i2c-%d) done\n", + tx_probe ? "Tx" : "Rx", adap->name, adap->nr); + mutex_unlock(&ir_devices_lock); + return 0; + +out_put_xx: + if (rx != NULL) + put_ir_rx(rx, true); + if (tx != NULL) + put_ir_tx(tx, true); +out_put_ir: + put_ir_device(ir, true); +out_no_ir: + zilog_error("%s: probing IR %s on %s (i2c-%d) failed with %d\n", + __func__, tx_probe ? "Tx" : "Rx", adap->name, adap->nr, + ret); + mutex_unlock(&ir_devices_lock); + return ret; +} + +static int __init zilog_init(void) +{ + int ret; + + zilog_notify("Zilog/Hauppauge IR driver initializing\n"); + + mutex_init(&tx_data_lock); + + request_module("firmware_class"); + + ret = i2c_add_driver(&driver); + if (ret) + zilog_error("initialization failed\n"); + else + zilog_notify("initialization complete\n"); + + return ret; +} + +static void __exit zilog_exit(void) +{ + i2c_del_driver(&driver); + /* if loaded */ + fw_unload(); + zilog_notify("Zilog/Hauppauge IR driver unloaded\n"); +} + +module_init(zilog_init); +module_exit(zilog_exit); + +MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)"); +MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, " + "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver, " + "Andy Walls"); +MODULE_LICENSE("GPL"); +/* for compat with old name, which isn't all that accurate anymore */ +MODULE_ALIAS("lirc_pvr150"); + +module_param(minor, int, 0444); +MODULE_PARM_DESC(minor, "Preferred minor device number"); + +module_param(debug, bool, 0644); +MODULE_PARM_DESC(debug, "Enable debugging messages"); + +module_param(tx_only, bool, 0644); +MODULE_PARM_DESC(tx_only, "Only handle the IR transmit function"); diff --git a/drivers/staging/media/solo6x10/Kconfig b/drivers/staging/media/solo6x10/Kconfig new file mode 100644 index 000000000000..03dcac4ea4d0 --- /dev/null +++ b/drivers/staging/media/solo6x10/Kconfig @@ -0,0 +1,8 @@ +config SOLO6X10 + tristate "Softlogic 6x10 MPEG codec cards" + depends on PCI && VIDEO_DEV && SND && I2C + select VIDEOBUF_DMA_SG + select SND_PCM + ---help--- + This driver supports the Softlogic based MPEG-4 and h.264 codec + codec cards. diff --git a/drivers/staging/media/solo6x10/Makefile b/drivers/staging/media/solo6x10/Makefile new file mode 100644 index 000000000000..72816cf16704 --- /dev/null +++ b/drivers/staging/media/solo6x10/Makefile @@ -0,0 +1,3 @@ +solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o + +obj-$(CONFIG_SOLO6X10) := solo6x10.o diff --git a/drivers/staging/media/solo6x10/TODO b/drivers/staging/media/solo6x10/TODO new file mode 100644 index 000000000000..7e6c4fa130df --- /dev/null +++ b/drivers/staging/media/solo6x10/TODO @@ -0,0 +1,24 @@ +TODO (staging => main): + + * Motion detection flags need to be moved to v4l2 + * Some private CIDs need to be moved to v4l2 + +TODO (general): + + * encoder on/off controls + * mpeg cid bitrate mode (vbr/cbr) + * mpeg cid bitrate/bitrate-peak + * mpeg encode of user data + * mpeg decode of user data + * switch between 4 frames/irq to 1 when using mjpeg (and then back + when not) + * implement a CID control for motion areas/thresholds + * implement CID controls for mozaic areas + * allow for higher level of interval (for < 1 fps) + * sound: + - implement playback via external sound jack + - implement loopback of external sound jack with incoming audio? + - implement pause/resume + +Plase send patches to Greg Kroah-Hartman and Cc Ben Collins + diff --git a/drivers/staging/media/solo6x10/core.c b/drivers/staging/media/solo6x10/core.c new file mode 100644 index 000000000000..f974f6412ad7 --- /dev/null +++ b/drivers/staging/media/solo6x10/core.c @@ -0,0 +1,332 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" +#include "tw28.h" + +MODULE_DESCRIPTION("Softlogic 6x10 MP4/H.264 Encoder/Decoder V4L2/ALSA Driver"); +MODULE_AUTHOR("Ben Collins "); +MODULE_VERSION(SOLO6X10_VERSION); +MODULE_LICENSE("GPL"); + +void solo_irq_on(struct solo_dev *solo_dev, u32 mask) +{ + solo_dev->irq_mask |= mask; + solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); +} + +void solo_irq_off(struct solo_dev *solo_dev, u32 mask) +{ + solo_dev->irq_mask &= ~mask; + solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); +} + +/* XXX We should check the return value of the sub-device ISR's */ +static irqreturn_t solo_isr(int irq, void *data) +{ + struct solo_dev *solo_dev = data; + u32 status; + int i; + + status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); + if (!status) + return IRQ_NONE; + + if (status & ~solo_dev->irq_mask) { + solo_reg_write(solo_dev, SOLO_IRQ_STAT, + status & ~solo_dev->irq_mask); + status &= solo_dev->irq_mask; + } + + if (status & SOLO_IRQ_PCI_ERR) { + u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR); + solo_p2m_error_isr(solo_dev, err); + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR); + } + + for (i = 0; i < SOLO_NR_P2M; i++) + if (status & SOLO_IRQ_P2M(i)) + solo_p2m_isr(solo_dev, i); + + if (status & SOLO_IRQ_IIC) + solo_i2c_isr(solo_dev); + + if (status & SOLO_IRQ_VIDEO_IN) + solo_video_in_isr(solo_dev); + + /* Call this first so enc gets detected flag set */ + if (status & SOLO_IRQ_MOTION) + solo_motion_isr(solo_dev); + + if (status & SOLO_IRQ_ENCODER) + solo_enc_v4l2_isr(solo_dev); + + if (status & SOLO_IRQ_G723) + solo_g723_isr(solo_dev); + + return IRQ_HANDLED; +} + +static void free_solo_dev(struct solo_dev *solo_dev) +{ + struct pci_dev *pdev; + + if (!solo_dev) + return; + + pdev = solo_dev->pdev; + + /* If we never initialized the PCI device, then nothing else + * below here needs cleanup */ + if (!pdev) { + kfree(solo_dev); + return; + } + + /* Bring down the sub-devices first */ + solo_g723_exit(solo_dev); + solo_enc_v4l2_exit(solo_dev); + solo_enc_exit(solo_dev); + solo_v4l2_exit(solo_dev); + solo_disp_exit(solo_dev); + solo_gpio_exit(solo_dev); + solo_p2m_exit(solo_dev); + solo_i2c_exit(solo_dev); + + /* Now cleanup the PCI device */ + if (solo_dev->reg_base) { + solo_irq_off(solo_dev, ~0); + pci_iounmap(pdev, solo_dev->reg_base); + free_irq(pdev->irq, solo_dev); + } + + pci_release_regions(pdev); + pci_disable_device(pdev); + pci_set_drvdata(pdev, NULL); + + kfree(solo_dev); +} + +static int __devinit solo_pci_probe(struct pci_dev *pdev, + const struct pci_device_id *id) +{ + struct solo_dev *solo_dev; + int ret; + int sdram; + u8 chip_id; + u32 reg; + + solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); + if (solo_dev == NULL) + return -ENOMEM; + + solo_dev->pdev = pdev; + spin_lock_init(&solo_dev->reg_io_lock); + pci_set_drvdata(pdev, solo_dev); + + ret = pci_enable_device(pdev); + if (ret) + goto fail_probe; + + pci_set_master(pdev); + + ret = pci_request_regions(pdev, SOLO6X10_NAME); + if (ret) + goto fail_probe; + + solo_dev->reg_base = pci_ioremap_bar(pdev, 0); + if (solo_dev->reg_base == NULL) { + ret = -ENOMEM; + goto fail_probe; + } + + chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & + SOLO_CHIP_ID_MASK; + switch (chip_id) { + case 7: + solo_dev->nr_chans = 16; + solo_dev->nr_ext = 5; + break; + case 6: + solo_dev->nr_chans = 8; + solo_dev->nr_ext = 2; + break; + default: + dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, " + "defaulting to 4 channels\n", + chip_id); + case 5: + solo_dev->nr_chans = 4; + solo_dev->nr_ext = 1; + } + + solo_dev->flags = id->driver_data; + + /* Disable all interrupts to start */ + solo_irq_off(solo_dev, ~0); + + reg = SOLO_SYS_CFG_SDRAM64BIT; + /* Initial global settings */ + if (!(solo_dev->flags & FLAGS_6110)) + reg |= SOLO6010_SYS_CFG_INPUTDIV(25) | + SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) | + SOLO6010_SYS_CFG_OUTDIV(3); + solo_reg_write(solo_dev, SOLO_SYS_CFG, reg); + + if (solo_dev->flags & FLAGS_6110) { + u32 sys_clock_MHz = SOLO_CLOCK_MHZ; + u32 pll_DIVQ; + u32 pll_DIVF; + + if (sys_clock_MHz < 125) { + pll_DIVQ = 3; + pll_DIVF = (sys_clock_MHz * 4) / 3; + } else { + pll_DIVQ = 2; + pll_DIVF = (sys_clock_MHz * 2) / 3; + } + + solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, + SOLO6110_PLL_RANGE_5_10MHZ | + SOLO6110_PLL_DIVR(9) | + SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) | + SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN); + mdelay(1); // PLL Locking time (1ms) + + solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */ + } else + solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */ + + solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1); + + /* PLL locking time of 1ms */ + mdelay(1); + + ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, + solo_dev); + if (ret) + goto fail_probe; + + /* Handle this from the start */ + solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); + + ret = solo_i2c_init(solo_dev); + if (ret) + goto fail_probe; + + /* Setup the DMA engine */ + sdram = (solo_dev->nr_chans >= 8) ? 2 : 1; + solo_reg_write(solo_dev, SOLO_DMA_CTRL, + SOLO_DMA_CTRL_REFRESH_CYCLE(1) | + SOLO_DMA_CTRL_SDRAM_SIZE(sdram) | + SOLO_DMA_CTRL_SDRAM_CLK_INVERT | + SOLO_DMA_CTRL_READ_CLK_SELECT | + SOLO_DMA_CTRL_LATENCY(1)); + + ret = solo_p2m_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_disp_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_gpio_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_tw28_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_v4l2_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_enc_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_enc_v4l2_init(solo_dev); + if (ret) + goto fail_probe; + + ret = solo_g723_init(solo_dev); + if (ret) + goto fail_probe; + + return 0; + +fail_probe: + free_solo_dev(solo_dev); + return ret; +} + +static void __devexit solo_pci_remove(struct pci_dev *pdev) +{ + struct solo_dev *solo_dev = pci_get_drvdata(pdev); + + free_solo_dev(solo_dev); +} + +static struct pci_device_id solo_id_table[] = { + /* 6010 based cards */ + {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)}, + {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), + .driver_data = FLAGS_6110}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)}, + /* 6110 based cards */ + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)}, + {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, solo_id_table); + +static struct pci_driver solo_pci_driver = { + .name = SOLO6X10_NAME, + .id_table = solo_id_table, + .probe = solo_pci_probe, + .remove = solo_pci_remove, +}; + +static int __init solo_module_init(void) +{ + return pci_register_driver(&solo_pci_driver); +} + +static void __exit solo_module_exit(void) +{ + pci_unregister_driver(&solo_pci_driver); +} + +module_init(solo_module_init); +module_exit(solo_module_exit); diff --git a/drivers/staging/media/solo6x10/disp.c b/drivers/staging/media/solo6x10/disp.c new file mode 100644 index 000000000000..884c0eb757c4 --- /dev/null +++ b/drivers/staging/media/solo6x10/disp.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" + +#define SOLO_VCLK_DELAY 3 +#define SOLO_PROGRESSIVE_VSIZE 1024 + +#define SOLO_MOT_THRESH_W 64 +#define SOLO_MOT_THRESH_H 64 +#define SOLO_MOT_THRESH_SIZE 8192 +#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) +#define SOLO_MOT_FLAG_SIZE 512 +#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32) + +static unsigned video_type; +module_param(video_type, uint, 0644); +MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)"); + +static void solo_vin_config(struct solo_dev *solo_dev) +{ + solo_dev->vin_hstart = 8; + solo_dev->vin_vstart = 2; + + solo_reg_write(solo_dev, SOLO_SYS_VCLK, + SOLO_VCLK_SELECT(2) | + SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | + SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, + SOLO_VI_H_START(solo_dev->vin_hstart) | + SOLO_VI_V_START(solo_dev->vin_vstart) | + SOLO_VI_V_STOP(solo_dev->vin_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, + SOLO_VI_H_START(solo_dev->vout_hstart) | + SOLO_VI_V_START(solo_dev->vout_vstart) | + SOLO_VI_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_ACT_P, + SOLO_VI_H_START(0) | + SOLO_VI_V_START(1) | + SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); + + solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, + SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); + + solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); + solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 240)); + } else { + solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, + SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); + solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, + SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, + SOLO_VI_PB_VSTART(4) | + SOLO_VI_PB_VSTOP(4 + 288)); + } + solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | + SOLO_VI_PB_HSTOP(16 + 720)); +} + +static void solo_disp_config(struct solo_dev *solo_dev) +{ + solo_dev->vout_hstart = 6; + solo_dev->vout_vstart = 8; + + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, + (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, + (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); + solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, + (16 << 24) | (128 << 16) | (16 << 8) | 128); + + solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, + solo_dev->video_type | + SOLO_VO_USER_COLOR_SET_NAV | + SOLO_VO_NA_COLOR_Y(0) | + SOLO_VO_NA_COLOR_CB(0) | + SOLO_VO_NA_COLOR_CR(0)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_H, + SOLO_VO_H_START(solo_dev->vout_hstart) | + SOLO_VO_H_STOP(solo_dev->vout_hstart + + solo_dev->video_hsize)); + + solo_reg_write(solo_dev, SOLO_VO_ACT_V, + SOLO_VO_V_START(solo_dev->vout_vstart) | + SOLO_VO_V_STOP(solo_dev->vout_vstart + + solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, + SOLO_VO_H_LEN(solo_dev->video_hsize) | + SOLO_VO_V_LEN(solo_dev->video_vsize)); + + solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5); + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | + SOLO_VO_DISP_ERASE_COUNT(8) | + SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); + + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + + /* Enable channels we support */ + solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1); + + /* Disable the watchdog */ + solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); +} + +static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, + u16 val, int reg_size) +{ + u16 buf[64]; + int i; + int ret = 0; + + for (i = 0; i < sizeof(buf) >> 1; i++) + buf[i] = val; + + for (i = 0; i < reg_size; i += sizeof(buf)) + ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf, + SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, + sizeof(buf)); + + return ret; +} + +void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) +{ + if (ch > solo_dev->nr_chans) + return; + + solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + (ch * SOLO_MOT_THRESH_SIZE * 2), + val, SOLO_MOT_THRESH_REAL); +} + +/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k + * threshold and working table for each channel. Atleast that's what the + * spec says. However, this code (take from rdk) has some mystery 8k + * block right after the flag area, before the first thresh table. */ +static void solo_motion_config(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + /* Clear motion flag area */ + solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, + SOLO_MOT_FLAG_SIZE); + + /* Clear working cache table */ + solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + + SOLO_MOT_THRESH_SIZE + + (i * SOLO_MOT_THRESH_SIZE * 2), + 0x0000, SOLO_MOT_THRESH_REAL); + + /* Set default threshold table */ + solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); + } + + /* Default motion settings */ + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, + SOLO_VI_MOTION_FRAME_COUNT(3) | + SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) + | /* SOLO_VI_MOTION_INTR_START_STOP | */ + SOLO_VI_MOTION_SAMPLE_COUNT(10)); + + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); +} + +int solo_disp_init(struct solo_dev *solo_dev) +{ + int i; + + solo_dev->video_hsize = 704; + if (video_type == 0) { + solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC; + solo_dev->video_vsize = 240; + solo_dev->fps = 30; + } else { + solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL; + solo_dev->video_vsize = 288; + solo_dev->fps = 25; + } + + solo_vin_config(solo_dev); + solo_motion_config(solo_dev); + solo_disp_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); + + return 0; +} + +void solo_disp_exit(struct solo_dev *solo_dev) +{ + int i; + + solo_irq_off(solo_dev, SOLO_IRQ_MOTION); + + solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); + solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); + solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); + } + + /* Set default border */ + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); + + for (i = 0; i < 5; i++) + solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); + + solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); + solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); + + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); + solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); +} diff --git a/drivers/staging/media/solo6x10/enc.c b/drivers/staging/media/solo6x10/enc.c new file mode 100644 index 000000000000..de502599bb19 --- /dev/null +++ b/drivers/staging/media/solo6x10/enc.c @@ -0,0 +1,238 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" +#include "osd-font.h" + +#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */ +#define OSG_BUFFER_SIZE 1024 + +#define VI_PROG_HSIZE (1280 - 16) +#define VI_PROG_VSIZE (1024 - 16) + +static void solo_capture_config(struct solo_dev *solo_dev) +{ + int i, j; + unsigned long height; + unsigned long width; + unsigned char *buf; + + solo_reg_write(solo_dev, SOLO_CAP_BASE, + SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE * + solo_dev->nr_chans) | + SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); + solo_reg_write(solo_dev, SOLO_CAP_BTW, + (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | + SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH)); + + /* Set scale 1, 9 dimension */ + width = solo_dev->video_hsize; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE1, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 2, 10 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize; + solo_reg_write(solo_dev, SOLO_DIM_SCALE2, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 3, 11 dimension */ + width = solo_dev->video_hsize / 2; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE3, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 4, 12 dimension */ + width = solo_dev->video_hsize / 3; + height = solo_dev->video_vsize / 3; + solo_reg_write(solo_dev, SOLO_DIM_SCALE4, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Set scale 5, 13 dimension */ + width = solo_dev->video_hsize / 4; + height = solo_dev->video_vsize / 2; + solo_reg_write(solo_dev, SOLO_DIM_SCALE5, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 8) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Progressive */ + width = VI_PROG_HSIZE; + height = VI_PROG_VSIZE; + solo_reg_write(solo_dev, SOLO_DIM_PROG, + SOLO_DIM_H_MB_NUM(width / 16) | + SOLO_DIM_V_MB_NUM_FRAME(height / 16) | + SOLO_DIM_V_MB_NUM_FIELD(height / 16)); + + /* Clear OSD */ + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); + solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); + solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, + 0xF0 << 16 | 0x80 << 8 | 0x80); + solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0); + + /* Clear OSG buffer */ + buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL); + if (!buf) + return; + + for (i = 0; i < solo_dev->nr_chans; i++) { + for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) { + solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf, + SOLO_EOSD_EXT_ADDR + + (i * SOLO_EOSD_EXT_SIZE) + j, + OSG_BUFFER_SIZE); + } + } + kfree(buf); +} + +int solo_osd_print(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + char *str = solo_enc->osd_text; + u8 *buf; + u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); + int len = strlen(str); + int i, j; + int x = 1, y = 1; + + if (len == 0) { + reg &= ~(1 << solo_enc->ch); + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); + return 0; + } + + buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + for (i = 0; i < len; i++) { + for (j = 0; j < 16; j++) { + buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] = + (solo_osd_font[(str[i] * 4) + (j / 4)] + >> ((3 - (j % 4)) * 8)) & 0xff; + } + } + + solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR + + (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE); + reg |= (1 << solo_enc->ch); + solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); + + kfree(buf); + + return 0; +} + +static void solo_jpeg_config(struct solo_dev *solo_dev) +{ + u32 reg; + if (solo_dev->flags & FLAGS_6110) + reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0); + else + reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0); + solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0); + solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, + (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | + ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); + solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); + /* que limit, samp limit, pos limit */ + solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60); +} + +static void solo_mp4e_config(struct solo_dev *solo_dev) +{ + int i; + u32 reg; + + /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */ + solo_reg_write(solo_dev, SOLO_VE_CFG0, + SOLO_VE_INTR_CTRL(0) | + SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | + SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); + + solo_reg_write(solo_dev, SOLO_VE_CFG1, + SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0)); + + solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); + solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); + solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); + + reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) | + SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15); + if (solo_dev->flags & FLAGS_6110) + reg |= SOLO_DCT_INTERVAL(10); + else + reg |= SOLO_DCT_INTERVAL(36 / 4); + solo_reg_write(solo_dev, SOLO_VE_ATTR, reg); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), + (SOLO_EREF_EXT_ADDR(solo_dev) + + (i * SOLO_EREF_EXT_SIZE)) >> 16); + + if (solo_dev->flags & FLAGS_6110) + solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */ +} + +int solo_enc_init(struct solo_dev *solo_dev) +{ + int i; + + solo_capture_config(solo_dev); + solo_mp4e_config(solo_dev); + solo_jpeg_config(solo_dev); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } + + solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); + + return 0; +} + +void solo_enc_exit(struct solo_dev *solo_dev) +{ + int i; + + solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); + } +} diff --git a/drivers/staging/media/solo6x10/g723.c b/drivers/staging/media/solo6x10/g723.c new file mode 100644 index 000000000000..59274bfca95b --- /dev/null +++ b/drivers/staging/media/solo6x10/g723.c @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" +#include "tw28.h" + +#define G723_INTR_ORDER 0 +#define G723_FDMA_PAGES 32 +#define G723_PERIOD_BYTES 48 +#define G723_PERIOD_BLOCK 1024 +#define G723_FRAMES_PER_PAGE 48 + +/* Sets up channels 16-19 for decoding and 0-15 for encoding */ +#define OUTMODE_MASK 0x300 + +#define SAMPLERATE 8000 +#define BITRATE 25 + +/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page + * is broken down to 20 * 48 byte regions (one for each channel possible) + * with the rest of the page being dummy data. */ +#define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) +#define IRQ_PAGES 4 /* 0 - 4 */ +#define PERIODS_MIN (1 << IRQ_PAGES) +#define PERIODS_MAX G723_FDMA_PAGES + +struct solo_snd_pcm { + int on; + spinlock_t lock; + struct solo_dev *solo_dev; + unsigned char g723_buf[G723_PERIOD_BYTES]; +}; + +static void solo_g723_config(struct solo_dev *solo_dev) +{ + int clk_div; + + clk_div = SOLO_CLOCK_MHZ / (SAMPLERATE * (BITRATE * 2) * 2); + + solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, + SOLO_AUDIO_BITRATE(BITRATE) | + SOLO_AUDIO_CLK_DIV(clk_div)); + + solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, + SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) | + SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) | + SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); + + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, + SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE | + SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK)); +} + +void solo_g723_isr(struct solo_dev *solo_dev) +{ + struct snd_pcm_str *pstr = + &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; + struct snd_pcm_substream *ss; + struct solo_snd_pcm *solo_pcm; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723); + + for (ss = pstr->substream; ss != NULL; ss = ss->next) { + if (snd_pcm_substream_chip(ss) == NULL) + continue; + + /* This means open() hasn't been called on this one */ + if (snd_pcm_substream_chip(ss) == solo_dev) + continue; + + /* Haven't triggered a start yet */ + solo_pcm = snd_pcm_substream_chip(ss); + if (!solo_pcm->on) + continue; + + snd_pcm_period_elapsed(ss); + } +} + +static int snd_solo_hw_params(struct snd_pcm_substream *ss, + struct snd_pcm_hw_params *hw_params) +{ + return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); +} + +static int snd_solo_hw_free(struct snd_pcm_substream *ss) +{ + return snd_pcm_lib_free_pages(ss); +} + +static struct snd_pcm_hardware snd_solo_pcm_hw = { + .info = (SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_MMAP_VALID), + .formats = SNDRV_PCM_FMTBIT_U8, + .rates = SNDRV_PCM_RATE_8000, + .rate_min = 8000, + .rate_max = 8000, + .channels_min = 1, + .channels_max = 1, + .buffer_bytes_max = MAX_BUFFER, + .period_bytes_min = G723_PERIOD_BYTES, + .period_bytes_max = G723_PERIOD_BYTES, + .periods_min = PERIODS_MIN, + .periods_max = PERIODS_MAX, +}; + +static int snd_solo_pcm_open(struct snd_pcm_substream *ss) +{ + struct solo_dev *solo_dev = snd_pcm_substream_chip(ss); + struct solo_snd_pcm *solo_pcm; + + solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); + if (solo_pcm == NULL) + return -ENOMEM; + + spin_lock_init(&solo_pcm->lock); + solo_pcm->solo_dev = solo_dev; + ss->runtime->hw = snd_solo_pcm_hw; + + snd_pcm_substream_chip(ss) = solo_pcm; + + return 0; +} + +static int snd_solo_pcm_close(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + + snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; + kfree(solo_pcm); + + return 0; +} + +static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + int ret = 0; + + spin_lock(&solo_pcm->lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + if (solo_pcm->on == 0) { + /* If this is the first user, switch on interrupts */ + if (atomic_inc_return(&solo_dev->snd_users) == 1) + solo_irq_on(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 1; + } + break; + case SNDRV_PCM_TRIGGER_STOP: + if (solo_pcm->on) { + /* If this was our last user, switch them off */ + if (atomic_dec_return(&solo_dev->snd_users) == 0) + solo_irq_off(solo_dev, SOLO_IRQ_G723); + solo_pcm->on = 0; + } + break; + default: + ret = -EINVAL; + } + + spin_unlock(&solo_pcm->lock); + + return ret; +} + +static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) +{ + return 0; +} + +static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; + + return idx * G723_FRAMES_PER_PAGE; +} + +static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, + snd_pcm_uframes_t pos, void __user *dst, + snd_pcm_uframes_t count) +{ + struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); + struct solo_dev *solo_dev = solo_pcm->solo_dev; + int err, i; + + for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { + int page = (pos / G723_FRAMES_PER_PAGE) + i; + + err = solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_G723E, 0, + solo_pcm->g723_buf, + SOLO_G723_EXT_ADDR(solo_dev) + + (page * G723_PERIOD_BLOCK) + + (ss->number * G723_PERIOD_BYTES), + G723_PERIOD_BYTES); + if (err) + return err; + + err = copy_to_user(dst + (i * G723_PERIOD_BYTES), + solo_pcm->g723_buf, G723_PERIOD_BYTES); + + if (err) + return -EFAULT; + } + + return 0; +} + +static struct snd_pcm_ops snd_solo_pcm_ops = { + .open = snd_solo_pcm_open, + .close = snd_solo_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_solo_hw_params, + .hw_free = snd_solo_hw_free, + .prepare = snd_solo_pcm_prepare, + .trigger = snd_solo_pcm_trigger, + .pointer = snd_solo_pcm_pointer, + .copy = snd_solo_pcm_copy, +}; + +static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *info) +{ + info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + info->count = 1; + info->value.integer.min = 0; + info->value.integer.max = 15; + info->value.integer.step = 1; + + return 0; +} + +static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + + value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); + + return 0; +} + +static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *value) +{ + struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); + u8 ch = value->id.numid - 1; + u8 old_val; + + old_val = tw28_get_audio_gain(solo_dev, ch); + if (old_val == value->value.integer.value[0]) + return 0; + + tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); + + return 1; +} + +static struct snd_kcontrol_new snd_solo_capture_volume = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Capture Volume", + .info = snd_solo_capture_volume_info, + .get = snd_solo_capture_volume_get, + .put = snd_solo_capture_volume_put, +}; + +static int solo_snd_pcm_init(struct solo_dev *solo_dev) +{ + struct snd_card *card = solo_dev->snd_card; + struct snd_pcm *pcm; + struct snd_pcm_substream *ss; + int ret; + int i; + + ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, + &pcm); + if (ret < 0) + return ret; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &snd_solo_pcm_ops); + + snd_pcm_chip(pcm) = solo_dev; + pcm->info_flags = 0; + strcpy(pcm->name, card->shortname); + + for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; + ss; ss = ss->next, i++) + sprintf(ss->name, "Camera #%d Audio", i); + + ret = snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + MAX_BUFFER, MAX_BUFFER); + if (ret < 0) + return ret; + + solo_dev->snd_pcm = pcm; + + return 0; +} + +int solo_g723_init(struct solo_dev *solo_dev) +{ + static struct snd_device_ops ops = { NULL }; + struct snd_card *card; + struct snd_kcontrol_new kctl; + char name[32]; + int ret; + + atomic_set(&solo_dev->snd_users, 0); + + /* Allows for easier mapping between video and audio */ + sprintf(name, "Softlogic%d", solo_dev->vfd->num); + + ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, + &solo_dev->snd_card); + if (ret < 0) + return ret; + + card = solo_dev->snd_card; + + strcpy(card->driver, SOLO6X10_NAME); + strcpy(card->shortname, "SOLO-6x10 Audio"); + sprintf(card->longname, "%s on %s IRQ %d", card->shortname, + pci_name(solo_dev->pdev), solo_dev->pdev->irq); + snd_card_set_dev(card, &solo_dev->pdev->dev); + + ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); + if (ret < 0) + goto snd_error; + + /* Mixer controls */ + strcpy(card->mixername, "SOLO-6x10"); + kctl = snd_solo_capture_volume; + kctl.count = solo_dev->nr_chans; + ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); + if (ret < 0) + return ret; + + ret = solo_snd_pcm_init(solo_dev); + if (ret < 0) + goto snd_error; + + ret = snd_card_register(card); + if (ret < 0) + goto snd_error; + + solo_g723_config(solo_dev); + + dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); + + return 0; + +snd_error: + snd_card_free(card); + return ret; +} + +void solo_g723_exit(struct solo_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); + solo_irq_off(solo_dev, SOLO_IRQ_G723); + + snd_card_free(solo_dev->snd_card); +} diff --git a/drivers/staging/media/solo6x10/gpio.c b/drivers/staging/media/solo6x10/gpio.c new file mode 100644 index 000000000000..0925e6f33a99 --- /dev/null +++ b/drivers/staging/media/solo6x10/gpio.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" + +static void solo_gpio_mode(struct solo_dev *solo_dev, + unsigned int port_mask, unsigned int mode) +{ + int port; + unsigned int ret; + + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); + + /* To set gpio */ + for (port = 0; port < 16; port++) { + if (!((1 << port) & port_mask)) + continue; + + ret &= (~(3 << (port << 1))); + ret |= ((mode & 3) << (port << 1)); + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); + + /* To set extended gpio - sensor */ + ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); + + for (port = 0; port < 16; port++) { + if (!((1 << (port + 16)) & port_mask)) + continue; + + if (!mode) + ret &= ~(1 << port); + else + ret |= 1 << port; + } + + solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); +} + +static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); +} + +static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value) +{ + solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, + solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); +} + +static void solo_gpio_config(struct solo_dev *solo_dev) +{ + /* Video reset */ + solo_gpio_mode(solo_dev, 0x30, 1); + solo_gpio_clear(solo_dev, 0x30); + udelay(100); + solo_gpio_set(solo_dev, 0x30); + udelay(100); + + /* Warning: Don't touch the next line unless you're sure of what + * you're doing: first four gpio [0-3] are used for video. */ + solo_gpio_mode(solo_dev, 0x0f, 2); + + /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ + solo_gpio_mode(solo_dev, 0xff00, 1); + + /* Initially set relay status to 0 */ + solo_gpio_clear(solo_dev, 0xff00); +} + +int solo_gpio_init(struct solo_dev *solo_dev) +{ + solo_gpio_config(solo_dev); + return 0; +} + +void solo_gpio_exit(struct solo_dev *solo_dev) +{ + solo_gpio_clear(solo_dev, 0x30); + solo_gpio_config(solo_dev); +} diff --git a/drivers/staging/media/solo6x10/i2c.c b/drivers/staging/media/solo6x10/i2c.c new file mode 100644 index 000000000000..ef95a500b4da --- /dev/null +++ b/drivers/staging/media/solo6x10/i2c.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c + * channel. The bus can only handle one i2c event at a time. The below handles + * this all wrong. We should be using the status registers to see if the bus + * is in use, and have a global lock to check the status register. Also, + * the bulk of the work should be handled out-of-interrupt. The ugly loops + * that occur during interrupt scare me. The ISR should merely signal + * thread context, ACK the interrupt, and move on. -- BenC */ + +#include +#include "solo6x10.h" + +u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) +{ + struct i2c_msg msgs[2]; + u8 data; + + msgs[0].flags = 0; + msgs[0].addr = addr; + msgs[0].len = 1; + msgs[0].buf = &off; + + msgs[1].flags = I2C_M_RD; + msgs[1].addr = addr; + msgs[1].len = 1; + msgs[1].buf = &data; + + i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); + + return data; +} + +void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, + u8 off, u8 data) +{ + struct i2c_msg msgs; + u8 buf[2]; + + buf[0] = off; + buf[1] = data; + msgs.flags = 0; + msgs.addr = addr; + msgs.len = 2; + msgs.buf = buf; + + i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); +} + +static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) +{ + u32 ctrl; + + ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); + + if (solo_dev->i2c_state == IIC_STATE_START) + ctrl |= SOLO_IIC_START; + + if (wr) { + ctrl |= SOLO_IIC_WRITE; + } else { + ctrl |= SOLO_IIC_READ; + if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) + ctrl |= SOLO_IIC_ACK_EN; + } + + if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) + ctrl |= SOLO_IIC_STOP; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); +} + +static void solo_i2c_start(struct solo_dev *solo_dev) +{ + u32 addr = solo_dev->i2c_msg->addr << 1; + + if (solo_dev->i2c_msg->flags & I2C_M_RD) + addr |= 1; + + solo_dev->i2c_state = IIC_STATE_START; + solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); + solo_i2c_flush(solo_dev, 1); +} + +static void solo_i2c_stop(struct solo_dev *solo_dev) +{ + solo_irq_off(solo_dev, SOLO_IRQ_IIC); + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo_dev->i2c_state = IIC_STATE_STOP; + wake_up(&solo_dev->i2c_wait); +} + +static int solo_i2c_handle_read(struct solo_dev *solo_dev) +{ +prepare_read: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_i2c_flush(solo_dev, 0); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + goto prepare_read; + else + solo_i2c_stop(solo_dev); + } + + return 0; +} + +static int solo_i2c_handle_write(struct solo_dev *solo_dev) +{ +retry_write: + if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { + solo_reg_write(solo_dev, SOLO_IIC_TXD, + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); + solo_dev->i2c_msg_ptr++; + solo_i2c_flush(solo_dev, 1); + return 0; + } + + solo_dev->i2c_msg_ptr = 0; + solo_dev->i2c_msg++; + solo_dev->i2c_msg_num--; + + if (solo_dev->i2c_msg_num == 0) { + solo_i2c_stop(solo_dev); + return 0; + } + + if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { + solo_i2c_start(solo_dev); + } else { + if (solo_dev->i2c_msg->flags & I2C_M_RD) + solo_i2c_stop(solo_dev); + else + goto retry_write; + } + + return 0; +} + +int solo_i2c_isr(struct solo_dev *solo_dev) +{ + u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); + int ret = -EINVAL; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC); + + if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) || + solo_dev->i2c_id < 0) { + solo_i2c_stop(solo_dev); + return -ENXIO; + } + + switch (solo_dev->i2c_state) { + case IIC_STATE_START: + if (solo_dev->i2c_msg->flags & I2C_M_RD) { + solo_dev->i2c_state = IIC_STATE_READ; + ret = solo_i2c_handle_read(solo_dev); + break; + } + + solo_dev->i2c_state = IIC_STATE_WRITE; + case IIC_STATE_WRITE: + ret = solo_i2c_handle_write(solo_dev); + break; + + case IIC_STATE_READ: + solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = + solo_reg_read(solo_dev, SOLO_IIC_RXD); + solo_dev->i2c_msg_ptr++; + + ret = solo_i2c_handle_read(solo_dev); + break; + + default: + solo_i2c_stop(solo_dev); + } + + return ret; +} + +static int solo_i2c_master_xfer(struct i2c_adapter *adap, + struct i2c_msg msgs[], int num) +{ + struct solo_dev *solo_dev = adap->algo_data; + unsigned long timeout; + int ret; + int i; + DEFINE_WAIT(wait); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (&solo_dev->i2c_adap[i] == adap) + break; + } + + if (i == SOLO_I2C_ADAPTERS) + return num; /* XXX Right return value for failure? */ + + mutex_lock(&solo_dev->i2c_mutex); + solo_dev->i2c_id = i; + solo_dev->i2c_msg = msgs; + solo_dev->i2c_msg_num = num; + solo_dev->i2c_msg_ptr = 0; + + solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); + solo_irq_on(solo_dev, SOLO_IRQ_IIC); + solo_i2c_start(solo_dev); + + timeout = HZ / 2; + + for (;;) { + prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE); + + if (solo_dev->i2c_state == IIC_STATE_STOP) + break; + + timeout = schedule_timeout(timeout); + if (!timeout) + break; + + if (signal_pending(current)) + break; + } + + finish_wait(&solo_dev->i2c_wait, &wait); + ret = num - solo_dev->i2c_msg_num; + solo_dev->i2c_state = IIC_STATE_IDLE; + solo_dev->i2c_id = -1; + + mutex_unlock(&solo_dev->i2c_mutex); + + return ret; +} + +static u32 solo_i2c_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_I2C; +} + +static struct i2c_algorithm solo_i2c_algo = { + .master_xfer = solo_i2c_master_xfer, + .functionality = solo_i2c_functionality, +}; + +int solo_i2c_init(struct solo_dev *solo_dev) +{ + int i; + int ret; + + solo_reg_write(solo_dev, SOLO_IIC_CFG, + SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); + + solo_dev->i2c_id = -1; + solo_dev->i2c_state = IIC_STATE_IDLE; + init_waitqueue_head(&solo_dev->i2c_wait); + mutex_init(&solo_dev->i2c_mutex); + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; + + snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i); + adap->algo = &solo_i2c_algo; + adap->algo_data = solo_dev; + adap->retries = 1; + adap->dev.parent = &solo_dev->pdev->dev; + + ret = i2c_add_adapter(adap); + if (ret) { + adap->algo_data = NULL; + break; + } + } + + if (ret) { + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + break; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } + return ret; + } + + dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n", + SOLO_I2C_ADAPTERS); + + return 0; +} + +void solo_i2c_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { + if (!solo_dev->i2c_adap[i].algo_data) + continue; + i2c_del_adapter(&solo_dev->i2c_adap[i]); + solo_dev->i2c_adap[i].algo_data = NULL; + } +} diff --git a/drivers/staging/media/solo6x10/jpeg.h b/drivers/staging/media/solo6x10/jpeg.h new file mode 100644 index 000000000000..50defec318cc --- /dev/null +++ b/drivers/staging/media/solo6x10/jpeg.h @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SOLO6X10_JPEG_H +#define __SOLO6X10_JPEG_H + +static unsigned char jpeg_header[] = { + 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, + 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, + 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, + 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, + 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, + 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50, + 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e, + 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, + 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, + 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0, + 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb, + 0x00, 0x43, 0x01, 0x22, 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, 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, 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, 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, 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, 0x02, 0xc0, + 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, + 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, + 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 +}; + +/* This is the byte marker for the start of SOF0: 0xffc0 marker */ +#define SOF0_START 575 + +#endif /* __SOLO6X10_JPEG_H */ diff --git a/drivers/staging/media/solo6x10/offsets.h b/drivers/staging/media/solo6x10/offsets.h new file mode 100644 index 000000000000..3d7e569f1cf8 --- /dev/null +++ b/drivers/staging/media/solo6x10/offsets.h @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SOLO6X10_OFFSETS_H +#define __SOLO6X10_OFFSETS_H + +/* Offsets and sizes of the external address */ +#define SOLO_DISP_EXT_ADDR 0x00000000 +#define SOLO_DISP_EXT_SIZE 0x00480000 + +#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) +#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000 + +#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE) +#define SOLO_OSG_EXT_SIZE 0x00120000 + +#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE) +#define SOLO_EOSD_EXT_SIZE 0x00010000 + +#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \ + (SOLO_EOSD_EXT_SIZE * __solo->nr_chans)) +#define SOLO_MOTION_EXT_SIZE 0x00080000 + +#define SOLO_G723_EXT_ADDR(__solo) \ + (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) +#define SOLO_G723_EXT_SIZE 0x00010000 + +#define SOLO_CAP_EXT_ADDR(__solo) \ + (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) +#define SOLO_CAP_EXT_MAX_PAGE (18 + 15) +#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536) + +/* This +1 is very important -- Why?! -- BenC */ +#define SOLO_EREF_EXT_ADDR(__solo) \ + (SOLO_CAP_EXT_ADDR(__solo) + \ + (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1))) +#define SOLO_EREF_EXT_SIZE 0x00140000 + +#define SOLO_MP4E_EXT_ADDR(__solo) \ + (SOLO_EREF_EXT_ADDR(__solo) + \ + (SOLO_EREF_EXT_SIZE * __solo->nr_chans)) +#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) + +#define SOLO_DREF_EXT_ADDR(__solo) \ + (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) +#define SOLO_DREF_EXT_SIZE 0x00140000 + +#define SOLO_MP4D_EXT_ADDR(__solo) \ + (SOLO_DREF_EXT_ADDR(__solo) + \ + (SOLO_DREF_EXT_SIZE * __solo->nr_chans)) +#define SOLO_MP4D_EXT_SIZE 0x00080000 + +#define SOLO_JPEG_EXT_ADDR(__solo) \ + (SOLO_MP4D_EXT_ADDR(__solo) + \ + (SOLO_MP4D_EXT_SIZE * __solo->nr_chans)) +#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) + +#endif /* __SOLO6X10_OFFSETS_H */ diff --git a/drivers/staging/media/solo6x10/osd-font.h b/drivers/staging/media/solo6x10/osd-font.h new file mode 100644 index 000000000000..591e0e82e0e8 --- /dev/null +++ b/drivers/staging/media/solo6x10/osd-font.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SOLO6X10_OSD_FONT_H +#define __SOLO6X10_OSD_FONT_H + +static const unsigned int solo_osd_font[] = { + 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000, + 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */ + 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000, + 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000, + 0x00000000, 0x00001038, 0x10000000, 0x00000000, + 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000, + 0x00000000, 0x00384444, 0x44380000, 0x00000000, + 0x00000000, 0x38448282, 0x82443800, 0x00000000, + 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000, + 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000, + 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000, + 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000, + 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000, + 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000, + 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000, + 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000, + 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000, + 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000, + 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000, + 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000, + 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000, + 0x00000082, 0x44288244, 0x38c6827c, 0x00000000, + 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000, + 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000, + 0x00000000, 0x3854929a, 0x82443800, 0x00000000, + 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000, + 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000, + 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000, + 0x0000007c, 0x82829292, 0x929282fe, 0x00000000, + 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000, + 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000, + 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */ + 0x00000066, 0x66240000, 0x00000000, 0x00000000, + 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */ + 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000, + 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */ + 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000, + 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */ + 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000, + 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */ + 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000, + 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */ + 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000, + 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */ + 0x00000000, 0x00000000, 0x00000006, 0x06000000, + 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */ + 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000, + 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */ + 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000, + 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */ + 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000, + 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */ + 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000, + 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */ + 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000, + 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */ + 0x00000000, 0x18180000, 0x00181800, 0x00000000, + 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */ + 0x00000060, 0x30180c06, 0x0c183060, 0x00000000, + 0x00000000, 0x007e0000, 0x007e0000, 0x00000000, + 0x00000006, 0x0c183060, 0x30180c06, 0x00000000, + 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000, + 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000, + 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */ + 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000, + 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */ + 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000, + 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */ + 0x000000fe, 0x0606063e, 0x06060606, 0x00000000, + 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */ + 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000, + 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */ + 0x00000060, 0x60606060, 0x6066663c, 0x00000000, + 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */ + 0x00000006, 0x06060606, 0x060606fe, 0x00000000, + 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */ + 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000, + 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */ + 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000, + 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */ + 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000, + 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */ + 0x0000007e, 0x18181818, 0x18181818, 0x00000000, + 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */ + 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000, + 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */ + 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000, + 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */ + 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000, + 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */ + 0x00000002, 0x060c1830, 0x60c08000, 0x00000000, + 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */ + 0x00001038, 0x6cc60000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00fe0000, + 0x00001818, 0x30000000, 0x00000000, 0x00000000, + 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000, + 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000, + 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000, + 0x00000060, 0x60607c66, 0x6666667c, 0x00000000, + 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000, + 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000, + 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00, + 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000, + 0x00000030, 0x30003830, 0x30303078, 0x00000000, + 0x00000030, 0x30003c30, 0x30303030, 0x30301f00, + 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000, + 0x00000030, 0x30303030, 0x30303030, 0x00000000, + 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000, + 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000, + 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000, + 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00, + 0x00000000, 0x00007c66, 0x6666667c, 0x60606000, + 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000, + 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000, + 0x00000000, 0x1818fe18, 0x18181870, 0x00000000, + 0x00000000, 0x00006666, 0x6666663c, 0x00000000, + 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000, + 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000, + 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000, + 0x00000000, 0x00006666, 0x6666667c, 0x60603e00, + 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000, + 0x00000070, 0x1818180e, 0x18181870, 0x00000000, + 0x00000018, 0x18181800, 0x18181818, 0x00000000, + 0x0000000e, 0x18181870, 0x1818180e, 0x00000000, + 0x000000dc, 0x76000000, 0x00000000, 0x00000000, + 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000 +}; + +#endif /* __SOLO6X10_OSD_FONT_H */ diff --git a/drivers/staging/media/solo6x10/p2m.c b/drivers/staging/media/solo6x10/p2m.c new file mode 100644 index 000000000000..56210f0fc5ec --- /dev/null +++ b/drivers/staging/media/solo6x10/p2m.c @@ -0,0 +1,306 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" + +/* #define SOLO_TEST_P2M */ + +int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, + void *sys_addr, u32 ext_addr, u32 size) +{ + dma_addr_t dma_addr; + int ret; + + WARN_ON(!size); + BUG_ON(id >= SOLO_NR_P2M); + + if (!size) + return -EINVAL; + + dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size); + + pci_unmap_single(solo_dev->pdev, dma_addr, size, + wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); + + return ret; +} + +int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size) +{ + struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA); + int ret; + + if (desc == NULL) + return -ENOMEM; + + solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0); + ret = solo_p2m_dma_desc(solo_dev, id, desc, 2); + kfree(desc); + + return ret; +} + +void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, + u32 ext_addr, u32 size, int repeat, u32 ext_size) +{ + desc->ta = cpu_to_le32(dma_addr); + desc->fa = cpu_to_le32(ext_addr); + + desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2)); + desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | + (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON); + + /* Ext size only matters when we're repeating */ + if (repeat) { + desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2)); + desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) | + SOLO_P2M_REPEAT(repeat)); + } +} + +int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id, + struct p2m_desc *desc, int desc_count) +{ + struct solo_p2m_dev *p2m_dev; + unsigned int timeout; + int ret = 0; + u32 config = 0; + dma_addr_t desc_dma = 0; + + BUG_ON(id >= SOLO_NR_P2M); + BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC); + + p2m_dev = &solo_dev->p2m_dev[id]; + + mutex_lock(&p2m_dev->mutex); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); + + INIT_COMPLETION(p2m_dev->completion); + p2m_dev->error = 0; + + /* Enable the descriptors */ + config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id)); + desc_dma = pci_map_single(solo_dev->pdev, desc, + desc_count * sizeof(*desc), + PCI_DMA_TODEVICE); + solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma); + solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config | + SOLO_P2M_DESC_MODE); + + /* Should have all descriptors completed from one interrupt */ + timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); + + /* Reset back to non-descriptor mode */ + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config); + solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0); + solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0); + pci_unmap_single(solo_dev->pdev, desc_dma, + desc_count * sizeof(*desc), + PCI_DMA_TODEVICE); + + if (p2m_dev->error) + ret = -EIO; + else if (timeout == 0) + ret = -EAGAIN; + + mutex_unlock(&p2m_dev->mutex); + + WARN_ON_ONCE(ret); + + return ret; +} + +int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id, + struct p2m_desc *pdesc, int wr, + struct scatterlist *sg, u32 sg_off, + u32 ext_addr, u32 size) +{ + int i; + int idx; + + BUG_ON(id >= SOLO_NR_P2M); + + if (WARN_ON_ONCE(!size)) + return -EINVAL; + + memset(pdesc, 0, sizeof(*pdesc)); + + /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */ + for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0; + i++, sg = sg_next(sg)) { + struct p2m_desc *desc = &pdesc[idx]; + u32 sg_len = sg_dma_len(sg); + u32 len; + + if (sg_off >= sg_len) { + sg_off -= sg_len; + continue; + } + + sg_len -= sg_off; + len = min(sg_len, size); + + solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off, + ext_addr, len, 0, 0); + + size -= len; + ext_addr += len; + idx++; + + sg_off = 0; + } + + WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC); + + return solo_p2m_dma_desc(solo_dev, id, pdesc, idx); +} + +#ifdef SOLO_TEST_P2M + +#define P2M_TEST_CHAR 0xbe + +static unsigned long long p2m_test(struct solo_dev *solo_dev, u8 id, + u32 base, int size) +{ + u8 *wr_buf; + u8 *rd_buf; + int i; + unsigned long long err_cnt = 0; + + wr_buf = kmalloc(size, GFP_KERNEL); + if (!wr_buf) { + printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); + return size; + } + + rd_buf = kmalloc(size, GFP_KERNEL); + if (!rd_buf) { + printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); + kfree(wr_buf); + return size; + } + + memset(wr_buf, P2M_TEST_CHAR, size); + memset(rd_buf, P2M_TEST_CHAR + 1, size); + + solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size); + solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size); + + for (i = 0; i < size; i++) + if (wr_buf[i] != rd_buf[i]) + err_cnt++; + + kfree(wr_buf); + kfree(rd_buf); + + return err_cnt; +} + +#define TEST_CHUNK_SIZE (8 * 1024) + +static void run_p2m_test(struct solo_dev *solo_dev) +{ + unsigned long long errs = 0; + u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); + int i, d; + + printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", + SOLO6X10_NAME, size); + + for (i = 0; i < size; i += TEST_CHUNK_SIZE) + for (d = 0; d < 4; d++) + errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); + + printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", + SOLO6X10_NAME, errs); + + return; +} +#else +#define run_p2m_test(__solo) do {} while (0) +#endif + +void solo_p2m_isr(struct solo_dev *solo_dev, int id) +{ + struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id)); + + complete(&p2m_dev->completion); +} + +void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status) +{ + struct solo_p2m_dev *p2m_dev; + int i; + + if (!(status & SOLO_PCI_ERR_P2M)) + return; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + p2m_dev->error = 1; + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + complete(&p2m_dev->completion); + } +} + +void solo_p2m_exit(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) + solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); +} + +int solo_p2m_init(struct solo_dev *solo_dev) +{ + struct solo_p2m_dev *p2m_dev; + int i; + + for (i = 0; i < SOLO_NR_P2M; i++) { + p2m_dev = &solo_dev->p2m_dev[i]; + + mutex_init(&p2m_dev->mutex); + init_completion(&p2m_dev->completion); + + solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); + solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), + SOLO_P2M_CSC_16BIT_565 | + SOLO_P2M_DMA_INTERVAL(3) | + SOLO_P2M_DESC_INTR_OPT | + SOLO_P2M_PCI_MASTER_MODE); + solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); + } + + run_p2m_test(solo_dev); + + return 0; +} diff --git a/drivers/staging/media/solo6x10/registers.h b/drivers/staging/media/solo6x10/registers.h new file mode 100644 index 000000000000..aca544472c93 --- /dev/null +++ b/drivers/staging/media/solo6x10/registers.h @@ -0,0 +1,637 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SOLO6X10_REGISTERS_H +#define __SOLO6X10_REGISTERS_H + +#include "offsets.h" + +/* Global 6X10 system configuration */ +#define SOLO_SYS_CFG 0x0000 +#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */ +#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */ +#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */ +#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */ +#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */ +#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */ +#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 +#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) +#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) +#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */ +#define SOLO_SYS_CFG_RESET 0x80000000 + +#define SOLO_DMA_CTRL 0x0004 +#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8) +/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */ +#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6) +#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5) +#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4) +#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) +#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) +#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) +#define SOLO_DMA_CTRL1 0x0008 + +#define SOLO_SYS_VCLK 0x000C +#define SOLO_VCLK_INVERT (1<<22) +/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */ +#define SOLO_VCLK_SELECT(n) ((n)<<20) +#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14) +#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12) +#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10) +#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8) +#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6) +#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4) +#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2) +#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) + +#define SOLO_IRQ_STAT 0x0010 +#define SOLO_IRQ_ENABLE 0x0014 +#define SOLO_IRQ_P2M(n) (1<<((n)+17)) +#define SOLO_IRQ_GPIO (1<<16) +#define SOLO_IRQ_VIDEO_LOSS (1<<15) +#define SOLO_IRQ_VIDEO_IN (1<<14) +#define SOLO_IRQ_MOTION (1<<13) +#define SOLO_IRQ_ATA_CMD (1<<12) +#define SOLO_IRQ_ATA_DIR (1<<11) +#define SOLO_IRQ_PCI_ERR (1<<10) +#define SOLO_IRQ_PS2_1 (1<<9) +#define SOLO_IRQ_PS2_0 (1<<8) +#define SOLO_IRQ_SPI (1<<7) +#define SOLO_IRQ_IIC (1<<6) +#define SOLO_IRQ_UART(n) (1<<((n) + 4)) +#define SOLO_IRQ_G723 (1<<3) +#define SOLO_IRQ_DECODER (1<<1) +#define SOLO_IRQ_ENCODER (1<<0) + +#define SOLO_CHIP_OPTION 0x001C +#define SOLO_CHIP_ID_MASK 0x00000007 + +#define SOLO6110_PLL_CONFIG 0x0020 +#define SOLO6110_PLL_RANGE_BYPASS (0 << 20) +#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20) +#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20) +#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20) +#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20) +#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20) +#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20) +#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20) +#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15) +#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12) +#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4) +#define SOLO6110_PLL_RESET (1 << 3) +#define SOLO6110_PLL_BYPASS (1 << 2) +#define SOLO6110_PLL_FSEN (1 << 1) +#define SOLO6110_PLL_FB (1 << 0) + +#define SOLO_EEPROM_CTRL 0x0060 +#define SOLO_EEPROM_ACCESS_EN (1<<7) +#define SOLO_EEPROM_CS (1<<3) +#define SOLO_EEPROM_CLK (1<<2) +#define SOLO_EEPROM_DO (1<<1) +#define SOLO_EEPROM_DI (1<<0) +#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS) + +#define SOLO_PCI_ERR 0x0070 +#define SOLO_PCI_ERR_FATAL 0x00000001 +#define SOLO_PCI_ERR_PARITY 0x00000002 +#define SOLO_PCI_ERR_TARGET 0x00000004 +#define SOLO_PCI_ERR_TIMEOUT 0x00000008 +#define SOLO_PCI_ERR_P2M 0x00000010 +#define SOLO_PCI_ERR_ATA 0x00000020 +#define SOLO_PCI_ERR_P2M_DESC 0x00000040 +#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f) +#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f) +#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f) + +#define SOLO_P2M_BASE 0x0080 + +#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20)) +#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */ +#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */ +/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */ +#define SOLO_P2M_CSC_16BIT_565 (1<<4) +#define SOLO_P2M_UV_SWAP (1<<3) +#define SOLO_P2M_PCI_MASTER_MODE (1<<2) +#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */ +#define SOLO_P2M_DESC_MODE (1<<0) + +#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20)) + +#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20)) +#define SOLO_P2M_UPDATE_ID(n) ((n)<<0) + +#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20)) +#define SOLO_P2M_COMMAND_DONE (1<<8) +#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat)) + +#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20)) +#define SOLO_P2M_PCI_INC(n) ((n)<<20) +#define SOLO_P2M_REPEAT(n) ((n)<<10) +/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */ +#define SOLO_P2M_BURST_SIZE(n) ((n)<<7) +#define SOLO_P2M_BURST_512 0 +#define SOLO_P2M_BURST_256 1 +#define SOLO_P2M_BURST_128 2 +#define SOLO_P2M_BURST_64 3 +#define SOLO_P2M_BURST_32 4 +#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */ +/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */ +#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4) +#define SOLO_P2M_CSC_ON (1<<3) +#define SOLO_P2M_INTERRUPT_REQ (1<<2) +#define SOLO_P2M_WRITE (1<<1) +#define SOLO_P2M_TRANS_ON (1<<0) + +#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20)) +#define SOLO_P2M_EXT_INC(n) ((n)<<20) +#define SOLO_P2M_COPY_SIZE(n) ((n)<<0) + +#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20)) + +#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20)) + +#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4)) + +#define SOLO_VI_CH_SWITCH_0 0x0100 +#define SOLO_VI_CH_SWITCH_1 0x0104 +#define SOLO_VI_CH_SWITCH_2 0x0108 + +#define SOLO_VI_CH_ENA 0x010C +#define SOLO_VI_CH_FORMAT 0x0110 +#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16) +#define SOLO_VI_PROG_MASK(n) ((n)<<0) + +#define SOLO_VI_FMT_CFG 0x0114 +#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31) +#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30) +#define SOLO_VI_FMT_TEST_SIGNAL (1<<28) + +#define SOLO_VI_PAGE_SW 0x0118 +#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8) +#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7) +#define SOLO_DISP_SYNC_FI(n) ((n)<<6) +#define SOLO_PIP_PAGE_ADD(n) ((n)<<3) +#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0) + +#define SOLO_VI_ACT_I_P 0x011C +#define SOLO_VI_ACT_I_S 0x0120 +#define SOLO_VI_ACT_P 0x0124 +#define SOLO_VI_FI_INVERT (1<<31) +#define SOLO_VI_H_START(n) ((n)<<21) +#define SOLO_VI_V_START(n) ((n)<<11) +#define SOLO_VI_V_STOP(n) ((n)<<0) + +#define SOLO_VI_STATUS0 0x0128 +#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07) +#define SOLO_VI_STATUS1 0x012C + +/* XXX: Might be better off in kernel level disp.h */ +#define DISP_PAGE(stat) ((stat) & 0x07) + +#define SOLO_VI_PB_CONFIG 0x0130 +#define SOLO_VI_PB_USER_MODE (1<<1) +#define SOLO_VI_PB_PAL (1<<0) +#define SOLO_VI_PB_RANGE_HV 0x0134 +#define SOLO_VI_PB_HSIZE(h) ((h)<<12) +#define SOLO_VI_PB_VSIZE(v) ((v)<<0) +#define SOLO_VI_PB_ACT_H 0x0138 +#define SOLO_VI_PB_HSTART(n) ((n)<<12) +#define SOLO_VI_PB_HSTOP(n) ((n)<<0) +#define SOLO_VI_PB_ACT_V 0x013C +#define SOLO_VI_PB_VSTART(n) ((n)<<12) +#define SOLO_VI_PB_VSTOP(n) ((n)<<0) + +#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4)) +#define SOLO_VI_MOSAIC_SX(x) ((x)<<24) +#define SOLO_VI_MOSAIC_EX(x) ((x)<<16) +#define SOLO_VI_MOSAIC_SY(x) ((x)<<8) +#define SOLO_VI_MOSAIC_EY(x) ((x)<<0) + +#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4)) +#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4)) + +#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28) + +#define SOLO_VI_WIN_PIP(n) ((n)<<27) +#define SOLO_VI_WIN_SCALE(n) ((n)<<24) + +#define SOLO_VI_WIN_SX(x) ((x)<<12) +#define SOLO_VI_WIN_EX(x) ((x)<<0) + +#define SOLO_VI_WIN_SY(x) ((x)<<12) +#define SOLO_VI_WIN_EY(x) ((x)<<0) + +#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4)) + +#define SOLO_VI_WIN_SW 0x0240 +#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244 + +#define SOLO_VI_MOT_ADR 0x0260 +#define SOLO_VI_MOTION_EN(mask) ((mask)<<16) +#define SOLO_VI_MOT_CTRL 0x0264 +#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24) +#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16) +#define SOLO_VI_MOTION_INTR_START_STOP (1<<15) +#define SOLO_VI_MOTION_FREEZE_DATA (1<<14) +#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0) +#define SOLO_VI_MOT_CLEAR 0x0268 +#define SOLO_VI_MOT_STATUS 0x026C +#define SOLO_VI_MOTION_CNT(n) ((n)<<0) +#define SOLO_VI_MOTION_BORDER 0x0270 +#define SOLO_VI_MOTION_BAR 0x0274 +#define SOLO_VI_MOTION_Y_SET (1<<29) +#define SOLO_VI_MOTION_Y_ADD (1<<28) +#define SOLO_VI_MOTION_CB_SET (1<<27) +#define SOLO_VI_MOTION_CB_ADD (1<<26) +#define SOLO_VI_MOTION_CR_SET (1<<25) +#define SOLO_VI_MOTION_CR_ADD (1<<24) +#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16) +#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8) +#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0) + +#define SOLO_VO_FMT_ENC 0x0300 +#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31) +#define SOLO_VO_FMT_TYPE_PAL (1<<30) +#define SOLO_VO_FMT_TYPE_NTSC 0 +#define SOLO_VO_USER_SET (1<<29) + +#define SOLO_VO_FI_CHANGE (1<<20) +#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) +#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) +#define SOLO_VO_USER_COLOR_SET_NAV (1<<17) +#define SOLO_VO_USER_COLOR_SET_NAH (1<<16) +#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) +#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) +#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) + +#define SOLO_VO_ACT_H 0x0304 +#define SOLO_VO_H_BLANK(n) ((n)<<22) +#define SOLO_VO_H_START(n) ((n)<<11) +#define SOLO_VO_H_STOP(n) ((n)<<0) + +#define SOLO_VO_ACT_V 0x0308 +#define SOLO_VO_V_BLANK(n) ((n)<<22) +#define SOLO_VO_V_START(n) ((n)<<11) +#define SOLO_VO_V_STOP(n) ((n)<<0) + +#define SOLO_VO_RANGE_HV 0x030C +#define SOLO_VO_SYNC_INVERT (1<<24) +#define SOLO_VO_HSYNC_INVERT (1<<23) +#define SOLO_VO_VSYNC_INVERT (1<<22) +#define SOLO_VO_H_LEN(n) ((n)<<11) +#define SOLO_VO_V_LEN(n) ((n)<<0) + +#define SOLO_VO_DISP_CTRL 0x0310 +#define SOLO_VO_DISP_ON (1<<31) +#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24) +#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22) +#define SOLO_VO_DISP_SINGLE_PAGE (1<<21) +#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff) + +#define SOLO_VO_DISP_ERASE 0x0314 +#define SOLO_VO_DISP_ERASE_ON (1<<0) + +#define SOLO_VO_ZOOM_CTRL 0x0318 +#define SOLO_VO_ZOOM_VER_ON (1<<24) +#define SOLO_VO_ZOOM_HOR_ON (1<<23) +#define SOLO_VO_ZOOM_V_COMP (1<<22) +#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11) +#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0) + +#define SOLO_VO_FREEZE_CTRL 0x031C +#define SOLO_VO_FREEZE_ON (1<<1) +#define SOLO_VO_FREEZE_INTERPOLATION (1<<0) + +#define SOLO_VO_BKG_COLOR 0x0320 +#define SOLO_BG_Y(y) ((y)<<16) +#define SOLO_BG_U(u) ((u)<<8) +#define SOLO_BG_V(v) ((v)<<0) + +#define SOLO_VO_DEINTERLACE 0x0324 +#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8) +#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) + +#define SOLO_VO_BORDER_LINE_COLOR 0x0330 +#define SOLO_VO_BORDER_FILL_COLOR 0x0334 +#define SOLO_VO_BORDER_LINE_MASK 0x0338 +#define SOLO_VO_BORDER_FILL_MASK 0x033c + +#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4)) +#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4)) + +#define SOLO_VO_CELL_EXT_SET 0x0368 +#define SOLO_VO_CELL_EXT_START 0x036c +#define SOLO_VO_CELL_EXT_STOP 0x0370 + +#define SOLO_VO_CELL_EXT_SET2 0x0374 +#define SOLO_VO_CELL_EXT_START2 0x0378 +#define SOLO_VO_CELL_EXT_STOP2 0x037c + +#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12)) +#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12)) +#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12)) + +#define SOLO_VO_CURSOR_POS (0x0380) +#define SOLO_VO_CURSOR_CLR (0x0384) +#define SOLO_VO_CURSOR_CLR2 (0x0388) +#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4)) + +#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4)) + +#define SOLO_OSG_CONFIG 0x03E0 +#define SOLO_VO_OSG_ON (1<<31) +#define SOLO_VO_OSG_COLOR_MUTE (1<<28) +#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22) +#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16) +#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff) + +#define SOLO_OSG_ERASE 0x03E4 +#define SOLO_OSG_ERASE_ON (0x80) +#define SOLO_OSG_ERASE_OFF (0x00) + +#define SOLO_VO_OSG_BLINK 0x03E8 +#define SOLO_VO_OSG_BLINK_ON (1<<1) +#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0) + +#define SOLO_CAP_BASE 0x0400 +#define SOLO_CAP_MAX_PAGE(n) ((n)<<16) +#define SOLO_CAP_BASE_ADDR(n) ((n)<<0) +#define SOLO_CAP_BTW 0x0404 +#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8) +#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0) + +#define SOLO_DIM_SCALE1 0x0408 +#define SOLO_DIM_SCALE2 0x040C +#define SOLO_DIM_SCALE3 0x0410 +#define SOLO_DIM_SCALE4 0x0414 +#define SOLO_DIM_SCALE5 0x0418 +#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16) +#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8) +#define SOLO_DIM_H_MB_NUM(n) ((n)<<0) + +#define SOLO_DIM_PROG 0x041C +#define SOLO_CAP_STATUS 0x0420 + +#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4)) +#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4)) +#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4)) +#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4)) + + +#define SOLO_VE_CFG0 0x0610 +#define SOLO_VE_TWO_PAGE_MODE (1<<31) +#define SOLO_VE_INTR_CTRL(n) ((n)<<24) +#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16) +#define SOLO_VE_BLOCK_BASE(n) ((n)<<0) + +#define SOLO_VE_CFG1 0x0614 +#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */ +#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */ +#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */ +#define SOLO_VE_INSERT_INDEX (1<<18) +#define SOLO_VE_MOTION_MODE(n) ((n)<<16) +#define SOLO_VE_MOTION_BASE(n) ((n)<<0) + +#define SOLO_VE_WMRK_POLY 0x061C +#define SOLO_VE_VMRK_INIT_KEY 0x0620 +#define SOLO_VE_WMRK_STRL 0x0624 +#define SOLO_VE_ENCRYP_POLY 0x0628 +#define SOLO_VE_ENCRYP_INIT 0x062C +#define SOLO_VE_ATTR 0x0630 +#define SOLO_VE_LITTLE_ENDIAN (1<<31) +#define SOLO_COMP_ATTR_RN (1<<30) +#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27) +#define SOLO_COMP_TIME_INC(n) ((n)<<25) +#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) +#define SOLO_DCT_INTERVAL(n) ((n)<<16) + +#define SOLO_VE_STATE(n) (0x0640+((n)*4)) + +#define SOLO_VE_JPEG_QP_TBL 0x0670 +#define SOLO_VE_JPEG_QP_CH_L 0x0674 +#define SOLO_VE_JPEG_QP_CH_H 0x0678 +#define SOLO_VE_JPEG_CFG 0x067C +#define SOLO_VE_JPEG_CTRL 0x0680 + +#define SOLO_VE_OSD_CH 0x0690 +#define SOLO_VE_OSD_BASE 0x0694 +#define SOLO_VE_OSD_CLR 0x0698 +#define SOLO_VE_OSD_OPT 0x069C + +#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4)) +#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */ +#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4)) +#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) +#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) +#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4)) +#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4)) +#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4)) + +#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8)) +#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) + +#define SOLO_VD_CFG0 0x0900 +#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */ +#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23) +#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) +#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) +#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20) +#define SOLO_VD_CFG_SINGLE_MODE (1<<18) +#define SOLO_VD_CFG_SCAL_MANUAL (1<<17) +#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16) +#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15) +#define SOLO_VD_CFG_START_FI (1<<14) +#define SOLO_VD_CFG_ERR_LOCK (1<<13) +#define SOLO_VD_CFG_ERR_INT_ENA (1<<12) +#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8) +#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0) + +#define SOLO_VD_CFG1 0x0904 + +#define SOLO_VD_DEINTERLACE 0x0908 +#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8) +#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) + +#define SOLO_VD_CODE_ADR 0x090C + +#define SOLO_VD_CTRL 0x0910 +#define SOLO_VD_OPER_ON (1<<31) +#define SOLO_VD_MAX_ITEM(n) ((n)<<0) + +#define SOLO_VD_STATUS0 0x0920 +#define SOLO_VD_STATUS0_INTR_ACK (1<<22) +#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21) +#define SOLO_VD_STATUS0_INTR_ERR (1<<20) + +#define SOLO_VD_STATUS1 0x0924 + +#define SOLO_VD_IDX0 0x0930 +#define SOLO_VD_IDX_INTERLACE (1<<30) +#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24) +#define SOLO_VD_IDX_SIZE(n) ((n)<<0) + +#define SOLO_VD_IDX1 0x0934 +#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28) +#define SOLO_VD_IDX_WINDOW(n) ((n)<<24) +#define SOLO_VD_IDX_DEINTERLACE (1<<16) +#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8) +#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0) + +#define SOLO_VD_IDX2 0x0938 +#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31) +#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff) + +#define SOLO_VD_IDX3 0x093C +#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28) +#define SOLO_VD_IDX_INTERLACE_WR (1<<27) +#define SOLO_VD_IDX_INTERPOL (1<<26) +#define SOLO_VD_IDX_HOR2X (1<<25) +#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12) +#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0) + +#define SOLO_VD_IDX4 0x0940 +#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8) +#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0) + +#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4)) + + +#define SOLO_GPIO_CONFIG_0 0x0B00 +#define SOLO_GPIO_CONFIG_1 0x0B04 +#define SOLO_GPIO_DATA_OUT 0x0B08 +#define SOLO_GPIO_DATA_IN 0x0B0C +#define SOLO_GPIO_INT_ACK_STA 0x0B10 +#define SOLO_GPIO_INT_ENA 0x0B14 +#define SOLO_GPIO_INT_CFG_0 0x0B18 +#define SOLO_GPIO_INT_CFG_1 0x0B1C + + +#define SOLO_IIC_CFG 0x0B20 +#define SOLO_IIC_ENABLE (1<<8) +#define SOLO_IIC_PRESCALE(n) ((n)<<0) + +#define SOLO_IIC_CTRL 0x0B24 +#define SOLO_IIC_AUTO_CLEAR (1<<20) +#define SOLO_IIC_STATE_RX_ACK (1<<19) +#define SOLO_IIC_STATE_BUSY (1<<18) +#define SOLO_IIC_STATE_SIG_ERR (1<<17) +#define SOLO_IIC_STATE_TRNS (1<<16) +#define SOLO_IIC_CH_SET(n) ((n)<<5) +#define SOLO_IIC_ACK_EN (1<<4) +#define SOLO_IIC_START (1<<3) +#define SOLO_IIC_STOP (1<<2) +#define SOLO_IIC_READ (1<<1) +#define SOLO_IIC_WRITE (1<<0) + +#define SOLO_IIC_TXD 0x0B28 +#define SOLO_IIC_RXD 0x0B2C + +/* + * UART REGISTER + */ +#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20)) +#define SOLO_UART_CLK_DIV(n) ((n)<<24) +#define SOLO_MODEM_CTRL_EN (1<<20) +#define SOLO_PARITY_ERROR_DROP (1<<18) +#define SOLO_IRQ_ERR_EN (1<<17) +#define SOLO_IRQ_RX_EN (1<<16) +#define SOLO_IRQ_TX_EN (1<<15) +#define SOLO_RX_EN (1<<14) +#define SOLO_TX_EN (1<<13) +#define SOLO_UART_HALF_DUPLEX (1<<12) +#define SOLO_UART_LOOPBACK (1<<11) + +#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6)) +#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6)) +#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6)) +#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6)) +#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6)) +#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6)) +#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6)) +#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6)) +#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6)) +#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6)) + +#define SOLO_UART_DATA_BIT_8 (3<<4) +#define SOLO_UART_DATA_BIT_7 (2<<4) +#define SOLO_UART_DATA_BIT_6 (1<<4) +#define SOLO_UART_DATA_BIT_5 (0<<4) + +#define SOLO_UART_STOP_BIT_1 (0<<2) +#define SOLO_UART_STOP_BIT_2 (1<<2) + +#define SOLO_UART_PARITY_NONE (0<<0) +#define SOLO_UART_PARITY_EVEN (2<<0) +#define SOLO_UART_PARITY_ODD (3<<0) + +#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20)) +#define SOLO_UART_CTS (1<<15) +#define SOLO_UART_RX_BUSY (1<<14) +#define SOLO_UART_OVERRUN (1<<13) +#define SOLO_UART_FRAME_ERR (1<<12) +#define SOLO_UART_PARITY_ERR (1<<11) +#define SOLO_UART_TX_BUSY (1<<5) + +#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f) +#define SOLO_UART_RX_BUFF_SIZE 8 +#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f) +#define SOLO_UART_TX_BUFF_SIZE 8 + +#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20)) +#define SOLO_UART_TX_DATA_PUSH (1<<8) +#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20)) +#define SOLO_UART_RX_DATA_POP (1<<8) + +#define SOLO_TIMER_CLOCK_NUM 0x0be0 +#define SOLO_TIMER_WATCHDOG 0x0be4 +#define SOLO_TIMER_USEC 0x0be8 +#define SOLO_TIMER_SEC 0x0bec + +#define SOLO_AUDIO_CONTROL 0x0D00 +#define SOLO_AUDIO_ENABLE (1<<31) +#define SOLO_AUDIO_MASTER_MODE (1<<30) +#define SOLO_AUDIO_I2S_MODE (1<<29) +#define SOLO_AUDIO_I2S_LR_SWAP (1<<27) +#define SOLO_AUDIO_I2S_8BIT (1<<26) +#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24) +#define SOLO_AUDIO_MIX_9TO0 (1<<23) +#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20) +#define SOLO_AUDIO_MIX_19TO10 (1<<19) +#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16) +#define SOLO_AUDIO_MODE(n) ((n)<<0) +#define SOLO_AUDIO_SAMPLE 0x0D04 +#define SOLO_AUDIO_EE_MODE_ON (1<<30) +#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25) +#define SOLO_AUDIO_BITRATE(n) ((n)<<16) +#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0) +#define SOLO_AUDIO_FDMA_INTR 0x0D08 +#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19) +#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16) +#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0) +#define SOLO_AUDIO_EVOL_0 0x0D0C +#define SOLO_AUDIO_EVOL_1 0x0D10 +#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) +#define SOLO_AUDIO_STA 0x0D14 + + +#define SOLO_WATCHDOG 0x0BE4 +#define WATCHDOG_STAT(status) (status<<8) +#define WATCHDOG_TIME(sec) (sec&0xff) + +#endif /* __SOLO6X10_REGISTERS_H */ diff --git a/drivers/staging/media/solo6x10/solo6x10.h b/drivers/staging/media/solo6x10/solo6x10.h new file mode 100644 index 000000000000..abee7213202f --- /dev/null +++ b/drivers/staging/media/solo6x10/solo6x10.h @@ -0,0 +1,336 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SOLO6X10_H +#define __SOLO6X10_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "registers.h" + +#ifndef PCI_VENDOR_ID_SOFTLOGIC +#define PCI_VENDOR_ID_SOFTLOGIC 0x9413 +#define PCI_DEVICE_ID_SOLO6010 0x6010 +#define PCI_DEVICE_ID_SOLO6110 0x6110 +#endif + +#ifndef PCI_VENDOR_ID_BLUECHERRY +#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3 +/* Neugent Softlogic 6010 based cards */ +#define PCI_DEVICE_ID_NEUSOLO_4 0x4304 +#define PCI_DEVICE_ID_NEUSOLO_9 0x4309 +#define PCI_DEVICE_ID_NEUSOLO_16 0x4310 +/* Bluecherry Softlogic 6010 based cards */ +#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04 +#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09 +#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10 +/* Bluecherry Softlogic 6110 based cards */ +#define PCI_DEVICE_ID_BC_6110_4 0x5304 +#define PCI_DEVICE_ID_BC_6110_8 0x5308 +#define PCI_DEVICE_ID_BC_6110_16 0x5310 +#endif /* Bluecherry */ + +#define SOLO6X10_NAME "solo6x10" + +#define SOLO_MAX_CHANNELS 16 + +/* Make sure these two match */ +#define SOLO6X10_VERSION "2.1.0" +#define SOLO6X10_VER_MAJOR 2 +#define SOLO6X10_VER_MINOR 0 +#define SOLO6X10_VER_SUB 0 +#define SOLO6X10_VER_NUM \ + KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB) + +#define FLAGS_6110 1 + +/* + * The SOLO6x10 actually has 8 i2c channels, but we only use 2. + * 0 - Techwell chip(s) + * 1 - SAA7128 + */ +#define SOLO_I2C_ADAPTERS 2 +#define SOLO_I2C_TW 0 +#define SOLO_I2C_SAA 1 + +/* DMA Engine setup */ +#define SOLO_NR_P2M 4 +#define SOLO_NR_P2M_DESC 256 +/* MPEG and JPEG share the same interrupt and locks so they must be together + * in the same dma channel. */ +#define SOLO_P2M_DMA_ID_MP4E 0 +#define SOLO_P2M_DMA_ID_JPEG 0 +#define SOLO_P2M_DMA_ID_MP4D 1 +#define SOLO_P2M_DMA_ID_G723D 1 +#define SOLO_P2M_DMA_ID_DISP 2 +#define SOLO_P2M_DMA_ID_OSG 2 +#define SOLO_P2M_DMA_ID_G723E 3 +#define SOLO_P2M_DMA_ID_VIN 3 + +/* Encoder standard modes */ +#define SOLO_ENC_MODE_CIF 2 +#define SOLO_ENC_MODE_HD1 1 +#define SOLO_ENC_MODE_D1 9 + +#define SOLO_DEFAULT_GOP 30 +#define SOLO_DEFAULT_QP 3 + +/* There is 8MB memory available for solo to buffer MPEG4 frames. + * This gives us 512 * 16kbyte queues. */ +#define SOLO_NR_RING_BUFS 512 + +#define SOLO_CLOCK_MHZ 108 + +#ifndef V4L2_BUF_FLAG_MOTION_ON +#define V4L2_BUF_FLAG_MOTION_ON 0x0400 +#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800 +#endif +#ifndef V4L2_CID_MOTION_ENABLE +#define PRIVATE_CIDS +#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0) +#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1) +#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2) +#endif + +enum SOLO_I2C_STATE { + IIC_STATE_IDLE, + IIC_STATE_START, + IIC_STATE_READ, + IIC_STATE_WRITE, + IIC_STATE_STOP +}; + +struct p2m_desc { + u32 ctrl; + u32 ext; + u32 ta; + u32 fa; +}; + +struct solo_p2m_dev { + struct mutex mutex; + struct completion completion; + int error; +}; + +#define OSD_TEXT_MAX 30 + +enum solo_enc_types { + SOLO_ENC_TYPE_STD, + SOLO_ENC_TYPE_EXT, +}; + +struct solo_enc_dev { + struct solo_dev *solo_dev; + /* V4L2 Items */ + struct video_device *vfd; + /* General accounting */ + wait_queue_head_t thread_wait; + spinlock_t lock; + atomic_t readers; + u8 ch; + u8 mode, gop, qp, interlaced, interval; + u8 reset_gop; + u8 bw_weight; + u8 motion_detected; + u16 motion_thresh; + u16 width; + u16 height; + char osd_text[OSD_TEXT_MAX + 1]; +}; + +struct solo_enc_buf { + u8 vop; + u8 ch; + enum solo_enc_types type; + u32 off; + u32 size; + u32 jpeg_off; + u32 jpeg_size; + struct timeval ts; +}; + +/* The SOLO6x10 PCI Device */ +struct solo_dev { + /* General stuff */ + struct pci_dev *pdev; + u8 __iomem *reg_base; + int nr_chans; + int nr_ext; + u32 flags; + u32 irq_mask; + u32 motion_mask; + spinlock_t reg_io_lock; + + /* tw28xx accounting */ + u8 tw2865, tw2864, tw2815; + u8 tw28_cnt; + + /* i2c related items */ + struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS]; + enum SOLO_I2C_STATE i2c_state; + struct mutex i2c_mutex; + int i2c_id; + wait_queue_head_t i2c_wait; + struct i2c_msg *i2c_msg; + unsigned int i2c_msg_num; + unsigned int i2c_msg_ptr; + + /* P2M DMA Engine */ + struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; + + /* V4L2 Display items */ + struct video_device *vfd; + unsigned int erasing; + unsigned int frame_blank; + u8 cur_disp_ch; + wait_queue_head_t disp_thread_wait; + + /* V4L2 Encoder items */ + struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS]; + u16 enc_bw_remain; + /* IDX into hw mp4 encoder */ + u8 enc_idx; + /* Our software ring of enc buf references */ + u16 enc_wr_idx; + struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS]; + + /* Current video settings */ + u32 video_type; + u16 video_hsize, video_vsize; + u16 vout_hstart, vout_vstart; + u16 vin_hstart, vin_vstart; + u8 fps; + + /* Audio components */ + struct snd_card *snd_card; + struct snd_pcm *snd_pcm; + atomic_t snd_users; + int g723_hw_idx; +}; + +static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) +{ + unsigned long flags; + u32 ret; + u16 val; + + spin_lock_irqsave(&solo_dev->reg_io_lock, flags); + + ret = readl(solo_dev->reg_base + reg); + rmb(); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); + rmb(); + + spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); + + return ret; +} + +static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) +{ + unsigned long flags; + u16 val; + + spin_lock_irqsave(&solo_dev->reg_io_lock, flags); + + writel(data, solo_dev->reg_base + reg); + wmb(); + pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); + rmb(); + + spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); +} + +void solo_irq_on(struct solo_dev *solo_dev, u32 mask); +void solo_irq_off(struct solo_dev *solo_dev, u32 mask); + +/* Init/exit routeines for subsystems */ +int solo_disp_init(struct solo_dev *solo_dev); +void solo_disp_exit(struct solo_dev *solo_dev); + +int solo_gpio_init(struct solo_dev *solo_dev); +void solo_gpio_exit(struct solo_dev *solo_dev); + +int solo_i2c_init(struct solo_dev *solo_dev); +void solo_i2c_exit(struct solo_dev *solo_dev); + +int solo_p2m_init(struct solo_dev *solo_dev); +void solo_p2m_exit(struct solo_dev *solo_dev); + +int solo_v4l2_init(struct solo_dev *solo_dev); +void solo_v4l2_exit(struct solo_dev *solo_dev); + +int solo_enc_init(struct solo_dev *solo_dev); +void solo_enc_exit(struct solo_dev *solo_dev); + +int solo_enc_v4l2_init(struct solo_dev *solo_dev); +void solo_enc_v4l2_exit(struct solo_dev *solo_dev); + +int solo_g723_init(struct solo_dev *solo_dev); +void solo_g723_exit(struct solo_dev *solo_dev); + +/* ISR's */ +int solo_i2c_isr(struct solo_dev *solo_dev); +void solo_p2m_isr(struct solo_dev *solo_dev, int id); +void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status); +void solo_enc_v4l2_isr(struct solo_dev *solo_dev); +void solo_g723_isr(struct solo_dev *solo_dev); +void solo_motion_isr(struct solo_dev *solo_dev); +void solo_video_in_isr(struct solo_dev *solo_dev); + +/* i2c read/write */ +u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off); +void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off, + u8 data); + +/* P2M DMA */ +int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, + dma_addr_t dma_addr, u32 ext_addr, u32 size); +int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, + void *sys_addr, u32 ext_addr, u32 size); +int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id, + struct p2m_desc *pdesc, int wr, + struct scatterlist *sglist, u32 sg_off, + u32 ext_addr, u32 size); +void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, + u32 ext_addr, u32 size, int repeat, u32 ext_size); +int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id, + struct p2m_desc *desc, int desc_count); + +/* Set the threshold for motion detection */ +void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val); +#define SOLO_DEF_MOT_THRESH 0x0300 + +/* Write text on OSD */ +int solo_osd_print(struct solo_enc_dev *solo_enc); + +#endif /* __SOLO6X10_H */ diff --git a/drivers/staging/media/solo6x10/tw28.c b/drivers/staging/media/solo6x10/tw28.c new file mode 100644 index 000000000000..db56b42c56c6 --- /dev/null +++ b/drivers/staging/media/solo6x10/tw28.c @@ -0,0 +1,821 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" +#include "tw28.h" + +/* XXX: Some of these values are masked into an 8-bit regs, and shifted + * around for other 8-bit regs. What are the magic bits in these values? */ +#define DEFAULT_HDELAY_NTSC (32 - 4) +#define DEFAULT_HACTIVE_NTSC (720 + 16) +#define DEFAULT_VDELAY_NTSC (7 - 2) +#define DEFAULT_VACTIVE_NTSC (240 + 4) + +#define DEFAULT_HDELAY_PAL (32 + 4) +#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) +#define DEFAULT_VDELAY_PAL (6) +#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) + +static u8 tbl_tw2864_template[] = { + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, + 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, + 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, + 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, + 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, +}; + +static u8 tbl_tw2865_ntsc_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ + 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, + 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, + 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +static u8 tbl_tw2865_pal_template[] = { + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ + 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, + 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ + 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, + 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ + 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, + 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ + 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, + 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ + 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, + 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ + 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, + 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ + 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, + 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ + 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, + 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ + 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, + 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ + 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, + 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ + 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, +}; + +#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) + +static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off, + u8 tw_off) +{ + if (is_tw286x(solo_dev, chip_id)) + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off); + else + return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off); +} + +static void tw_writebyte(struct solo_dev *solo_dev, int chip_id, + u8 tw6x_off, u8 tw_off, u8 val) +{ + if (is_tw286x(solo_dev, chip_id)) + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw6x_off, val); + else + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_id), + tw_off, val); +} + +static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, + u8 val) +{ + int i; + + for (i = 0; i < 5; i++) { + u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); + if (rval == val) + return; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); + msleep_interruptible(1); + } + +/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", + addr, off, val); */ +} + +static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2865_common[256]; + int i; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) + memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, + sizeof(tbl_tw2865_common)); + else + memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, + sizeof(tbl_tw2865_common)); + + /* ALINK Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2865_common[0xd2] = 0x01; + tbl_tw2865_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2865_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2865_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2865_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2865_common[0xcf] = 0x80; + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + if (i >= 0xb8 && i <= 0xc1) + continue; + if ((i & ~0x30) == 0x00 || + (i & ~0x30) == 0x0c || + (i & ~0x30) == 0x0d) + continue; + if (i >= 0xc4 && i <= 0xc7) + continue; + if (i == 0xfd) + continue; + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2865_common[i]); + } + + return 0; +} + +static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)]; + int i; + + memcpy(tbl_tw2864_common, tbl_tw2864_template, + sizeof(tbl_tw2864_common)); + + if (solo_dev->tw2865 == 0) { + /* IRQ Mode */ + if (solo_dev->nr_chans == 4) { + tbl_tw2864_common[0xd2] = 0x01; + tbl_tw2864_common[0xcf] = 0x00; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x40; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x43; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x40; + } + } else { + /* ALINK Mode. Assumes that the first tw28xx is a + * 2865 and these are in cascade. */ + for (i = 0; i <= 4; i++) + tbl_tw2864_common[0x08 | i << 4] = 0x12; + + if (solo_dev->nr_chans == 8) { + tbl_tw2864_common[0xd2] = 0x02; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x80; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2864_common[0xd2] = 0x03; + if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2864_common[0xcf] = 0x83; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2864_common[0xcf] = 0x80; + } + } + + /* NTSC or PAL */ + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) { + for (i = 0; i < 4; i++) { + tbl_tw2864_common[0x07 | (i << 4)] |= 0x10; + tbl_tw2864_common[0x08 | (i << 4)] |= 0x06; + tbl_tw2864_common[0x0a | (i << 4)] |= 0x08; + tbl_tw2864_common[0x0b | (i << 4)] |= 0x13; + tbl_tw2864_common[0x0e | (i << 4)] |= 0x01; + } + tbl_tw2864_common[0x9d] = 0x90; + tbl_tw2864_common[0xf3] = 0x00; + tbl_tw2864_common[0xf4] = 0xa0; + } + + for (i = 0; i < 0xff; i++) { + /* Skip read only registers */ + if (i >= 0xb8 && i <= 0xc1) + continue; + if ((i & ~0x30) == 0x00 || + (i & ~0x30) == 0x0c || + (i & ~0x30) == 0x0d) + continue; + if (i == 0x74 || i == 0x77 || i == 0x78 || + i == 0x79 || i == 0x7a) + continue; + if (i == 0xfd) + continue; + + tw_write_and_verify(solo_dev, dev_addr, i, + tbl_tw2864_common[i]); + } + + return 0; +} + +static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr) +{ + u8 tbl_ntsc_tw2815_common[] = { + 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, + 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, + }; + + u8 tbl_pal_tw2815_common[] = { + 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, + 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, + }; + + u8 tbl_tw2815_sfr[] = { + 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ + 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ + 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, + 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ + 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, + 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ + }; + u8 *tbl_tw2815_common; + int i; + int ch; + + tbl_ntsc_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); + + /* Horizontal Active Control */ + tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); + + /* Vertical Active Control */ + tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; + tbl_ntsc_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); + + tbl_pal_tw2815_common[0x06] = 0; + + /* Horizontal Delay Control */ + tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); + + /* Horizontal Active Control */ + tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); + + /* Vertical Delay Control */ + tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); + + /* Vertical Active Control */ + tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; + tbl_pal_tw2815_common[0x06] |= + ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); + + tbl_tw2815_common = + (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? + tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; + + /* Dual ITU-R BT.656 format */ + tbl_tw2815_common[0x0d] |= 0x04; + + /* Audio configuration */ + tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); + + if (solo_dev->nr_chans == 4) { + tbl_tw2815_sfr[0x63 - 0x40] |= 1; + tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; + } else if (solo_dev->nr_chans == 8) { + tbl_tw2815_sfr[0x63 - 0x40] |= 2; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } else if (solo_dev->nr_chans == 16) { + tbl_tw2815_sfr[0x63 - 0x40] |= 3; + if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) + tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; + else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) + tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; + } + + /* Output mode of R_ADATM pin (0 mixing, 1 record) */ + /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ + + /* 8KHz, used to be 16KHz, but changed for remote client compat */ + tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; + tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; + + /* Playback of right channel */ + tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; + + /* Reserved value (XXX ??) */ + tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; + + /* Analog output gain and mix ratio playback on full */ + tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; + /* Select playback audio and mute all except */ + tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; + tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; + + /* End of audio configuration */ + + for (ch = 0; ch < 4; ch++) { + tbl_tw2815_common[0x0d] &= ~3; + switch (ch) { + case 0: + tbl_tw2815_common[0x0d] |= 0x21; + break; + case 1: + tbl_tw2815_common[0x0d] |= 0x20; + break; + case 2: + tbl_tw2815_common[0x0d] |= 0x23; + break; + case 3: + tbl_tw2815_common[0x0d] |= 0x22; + break; + } + + for (i = 0; i < 0x0f; i++) { + if (i == 0x00) + continue; /* read-only */ + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + dev_addr, (ch * 0x10) + i, + tbl_tw2815_common[i]); + } + } + + for (i = 0x40; i < 0x76; i++) { + /* Skip read-only and nop registers */ + if (i == 0x40 || i == 0x59 || i == 0x5a || + i == 0x5d || i == 0x5e || i == 0x5f) + continue; + + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, + tbl_tw2815_sfr[i - 0x40]); + } + + return 0; +} + +#define FIRST_ACTIVE_LINE 0x0008 +#define LAST_ACTIVE_LINE 0x0102 + +static void saa7128_setup(struct solo_dev *solo_dev) +{ + int i; + unsigned char regs[128] = { + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00, + 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, + 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, + 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, + 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, + 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, + 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, + 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00, + 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, + 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff, + }; + + regs[0x7A] = FIRST_ACTIVE_LINE & 0xff; + regs[0x7B] = LAST_ACTIVE_LINE & 0xff; + regs[0x7C] = ((1 << 7) | + (((LAST_ACTIVE_LINE >> 8) & 1) << 6) | + (((FIRST_ACTIVE_LINE >> 8) & 1) << 4)); + + /* PAL: XXX: We could do a second set of regs to avoid this */ + if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) { + regs[0x28] = 0xE1; + + regs[0x5A] = 0x0F; + regs[0x61] = 0x02; + regs[0x62] = 0x35; + regs[0x63] = 0xCB; + regs[0x64] = 0x8A; + regs[0x65] = 0x09; + regs[0x66] = 0x2A; + + regs[0x6C] = 0xf1; + regs[0x6E] = 0x20; + + regs[0x7A] = 0x06 + 12; + regs[0x7b] = 0x24 + 12; + regs[0x7c] |= 1 << 6; + } + + /* First 0x25 bytes are read-only? */ + for (i = 0x26; i < 128; i++) { + if (i == 0x60 || i == 0x7D) + continue; + solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]); + } + + return; +} + +int solo_tw28_init(struct solo_dev *solo_dev) +{ + int i; + u8 value; + + /* Detect techwell chip type */ + for (i = 0; i < TW_NUM_CHIP; i++) { + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), 0xFF); + + switch (value >> 3) { + case 0x18: + solo_dev->tw2865 |= 1 << i; + solo_dev->tw28_cnt++; + break; + case 0x0c: + solo_dev->tw2864 |= 1 << i; + solo_dev->tw28_cnt++; + break; + default: + value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(i), 0x59); + if ((value >> 3) == 0x04) { + solo_dev->tw2815 |= 1 << i; + solo_dev->tw28_cnt++; + } + } + } + + if (!solo_dev->tw28_cnt) + return -EINVAL; + + saa7128_setup(solo_dev); + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + if ((solo_dev->tw2865 & (1 << i))) + tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else if ((solo_dev->tw2864 & (1 << i))) + tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + else + tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); + } + + dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:", + solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s"); + + if (solo_dev->tw2865) + printk(" tw2865[%d]", hweight32(solo_dev->tw2865)); + if (solo_dev->tw2864) + printk(" tw2864[%d]", hweight32(solo_dev->tw2864)); + if (solo_dev->tw2815) + printk(" tw2815[%d]", hweight32(solo_dev->tw2815)); + printk("\n"); + + return 0; +} + +/* + * We accessed the video status signal in the Techwell chip through + * iic/i2c because the video status reported by register REG_VI_STATUS1 + * (address 0x012C) of the SOLO6010 chip doesn't give the correct video + * status signal values. + */ +int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) +{ + u8 val, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0x0f; + + return val & (1 << ch) ? 1 : 0; +} + +#if 0 +/* Status of audio from up to 4 techwell chips are combined into 1 variable. + * See techwell datasheet for details. */ +u16 tw28_get_audio_status(struct solo_dev *solo_dev) +{ + u8 val; + u16 status = 0; + int i; + + for (i = 0; i < solo_dev->tw28_cnt; i++) { + val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR, + TW_AV_STAT_ADDR) & 0xf0) >> 4; + status |= val << (i * 4); + } + + return status; +} +#endif + +int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val) +{ + char sval; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + if (val > 255 || val < 0) + return -ERANGE; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (val > 0x0f || val < 0) + return -ERANGE; + if (is_tw286x(solo_dev, chip_num)) { + u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + v &= 0xf0; + v |= val; + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num), v); + } else if (val != 0) + return -ERANGE; + break; + + case V4L2_CID_HUE: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch), sval); + + break; + + case V4L2_CID_SATURATION: + if (is_tw286x(solo_dev, chip_num)) { + solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SATURATIONU_ADDR(ch), val); + } + tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), + TW_SATURATION_ADDR(ch), val); + + break; + + case V4L2_CID_CONTRAST: + tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch), val); + break; + + case V4L2_CID_BRIGHTNESS: + if (is_tw286x(solo_dev, chip_num)) + sval = val - 128; + else + sval = (char)val; + tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch), sval); + + break; + default: + return -EINVAL; + } + + return 0; +} + +int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, + s32 *val) +{ + u8 rval, chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + switch (ctrl) { + case V4L2_CID_SHARPNESS: + /* Only 286x has sharpness */ + if (is_tw286x(solo_dev, chip_num)) { + rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, + TW_CHIP_OFFSET_ADDR(chip_num), + TW286x_SHARPNESS(chip_num)); + *val = rval & 0x0f; + } else + *val = 0; + break; + case V4L2_CID_HUE: + rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), + TW_HUE_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + case V4L2_CID_SATURATION: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_SATURATIONU_ADDR(ch), + TW_SATURATION_ADDR(ch)); + break; + case V4L2_CID_CONTRAST: + *val = tw_readbyte(solo_dev, chip_num, + TW286x_CONTRAST_ADDR(ch), + TW_CONTRAST_ADDR(ch)); + break; + case V4L2_CID_BRIGHTNESS: + rval = tw_readbyte(solo_dev, chip_num, + TW286x_BRIGHTNESS_ADDR(ch), + TW_BRIGHTNESS_ADDR(ch)); + if (is_tw286x(solo_dev, chip_num)) + *val = (s32)((char)rval) + 128; + else + *val = rval; + break; + default: + return -EINVAL; + } + + return 0; +} + +#if 0 +/* + * For audio output volume, the output channel is only 1. In this case we + * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used + * is the base address of the techwell chip. + */ +void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val) +{ + unsigned int val; + unsigned int chip_num; + + chip_num = (solo_dev->nr_chans - 1) / 4; + + val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR); + + u_val = (val & 0x0f) | (u_val << 4); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, + TW_AUDIO_OUTPUT_VOL_ADDR, u_val); +} +#endif + +u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch) +{ + u8 val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + return (ch % 2) ? (val >> 4) : (val & 0x0f); +} + +void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val) +{ + u8 old_val; + u8 chip_num; + + /* Get the right chip and on-chip channel */ + chip_num = ch / 4; + ch %= 4; + + old_val = tw_readbyte(solo_dev, chip_num, + TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch)); + + val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | + ((ch % 2) ? (val << 4) : val); + + tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), + TW_AUDIO_INPUT_GAIN_ADDR(ch), val); +} diff --git a/drivers/staging/media/solo6x10/tw28.h b/drivers/staging/media/solo6x10/tw28.h new file mode 100644 index 000000000000..a44a03afbd30 --- /dev/null +++ b/drivers/staging/media/solo6x10/tw28.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 __SOLO6X10_TW28_H +#define __SOLO6X10_TW28_H + +#include "solo6x10.h" + +#define TW_NUM_CHIP 4 +#define TW_BASE_ADDR 0x28 +#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n)) + +/* tw2815 */ +#define TW_AV_STAT_ADDR 0x5a +#define TW_HUE_ADDR(n) (0x07 | ((n) << 4)) +#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4)) +#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4)) +#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4)) +#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70 +#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) + +/* tw286x */ +#define TW286X_AV_STAT_ADDR 0xfd +#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) +#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) +#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) +#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4)) +#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4)) +#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4)) +#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf +#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0)) + +int solo_tw28_init(struct solo_dev *solo_dev); + +int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val); +int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val); + +u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch); +void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val); +int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch); + +#if 0 +unsigned int tw2815_get_audio_status(struct SOLO *solo); +void tw2815_Set_AudioOutVol(struct SOLO *solo, unsigned int u_val); +#endif + +#endif /* __SOLO6X10_TW28_H */ diff --git a/drivers/staging/media/solo6x10/v4l2-enc.c b/drivers/staging/media/solo6x10/v4l2-enc.c new file mode 100644 index 000000000000..bee7280bbed9 --- /dev/null +++ b/drivers/staging/media/solo6x10/v4l2-enc.c @@ -0,0 +1,1825 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" +#include "tw28.h" +#include "jpeg.h" + +#define MIN_VID_BUFFERS 4 +#define FRAME_BUF_SIZE (128 * 1024) +#define MP4_QS 16 + +static int solo_enc_thread(void *data); + +extern unsigned video_nr; + +struct solo_enc_fh { + struct solo_enc_dev *enc; + u32 fmt; + u16 rd_idx; + u8 enc_on; + enum solo_enc_types type; + struct videobuf_queue vidq; + struct list_head vidq_active; + struct task_struct *kthread; + struct p2m_desc desc[SOLO_NR_P2M_DESC]; +}; + +static const u32 solo_user_ctrls[] = { + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + V4L2_CID_SHARPNESS, + 0 +}; + +static const u32 solo_mpeg_ctrls[] = { + V4L2_CID_MPEG_VIDEO_ENCODING, + V4L2_CID_MPEG_VIDEO_GOP_SIZE, + 0 +}; + +static const u32 solo_private_ctrls[] = { + V4L2_CID_MOTION_ENABLE, + V4L2_CID_MOTION_THRESHOLD, + 0 +}; + +static const u32 solo_fmtx_ctrls[] = { + V4L2_CID_RDS_TX_RADIO_TEXT, + 0 +}; + +static const u32 *solo_ctrl_classes[] = { + solo_user_ctrls, + solo_mpeg_ctrls, + solo_fmtx_ctrls, + solo_private_ctrls, + NULL +}; + +static int solo_is_motion_on(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + u8 ch = solo_enc->ch; + + if (solo_dev->motion_mask & (1 << ch)) + return 1; + return 0; +} + +static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + u8 ch = solo_enc->ch; + + spin_lock(&solo_enc->lock); + + if (on) + solo_dev->motion_mask |= (1 << ch); + else + solo_dev->motion_mask &= ~(1 << ch); + + /* Do this regardless of if we are turning on or off */ + solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, + 1 << solo_enc->ch); + solo_enc->motion_detected = 0; + + solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, + SOLO_VI_MOTION_EN(solo_dev->motion_mask) | + (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); + + if (solo_dev->motion_mask) + solo_irq_on(solo_dev, SOLO_IRQ_MOTION); + else + solo_irq_off(solo_dev, SOLO_IRQ_MOTION); + + spin_unlock(&solo_enc->lock); +} + +/* Should be called with solo_enc->lock held */ +static void solo_update_mode(struct solo_enc_dev *solo_enc) +{ + struct solo_dev *solo_dev = solo_enc->solo_dev; + + assert_spin_locked(&solo_enc->lock); + + solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; + solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); + + switch (solo_enc->mode) { + case SOLO_ENC_MODE_CIF: + solo_enc->width = solo_dev->video_hsize >> 1; + solo_enc->height = solo_dev->video_vsize; + break; + case SOLO_ENC_MODE_D1: + solo_enc->width = solo_dev->video_hsize; + solo_enc->height = solo_dev->video_vsize << 1; + solo_enc->bw_weight <<= 2; + break; + default: + WARN(1, "mode is unknown\n"); + } +} + +/* Should be called with solo_enc->lock held */ +static int solo_enc_on(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + u8 ch = solo_enc->ch; + struct solo_dev *solo_dev = solo_enc->solo_dev; + u8 interval; + + assert_spin_locked(&solo_enc->lock); + + if (fh->enc_on) + return 0; + + solo_update_mode(solo_enc); + + /* Make sure to bw check on first reader */ + if (!atomic_read(&solo_enc->readers)) { + if (solo_enc->bw_weight > solo_dev->enc_bw_remain) + return -EBUSY; + else + solo_dev->enc_bw_remain -= solo_enc->bw_weight; + } + + fh->enc_on = 1; + fh->rd_idx = solo_enc->solo_dev->enc_wr_idx; + + if (fh->type == SOLO_ENC_TYPE_EXT) + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); + + if (atomic_inc_return(&solo_enc->readers) > 1) + return 0; + + /* Disable all encoding for this channel */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); + + /* Common for both std and ext encoding */ + solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), + solo_enc->interlaced ? 1 : 0); + + if (solo_enc->interlaced) + interval = solo_enc->interval - 1; + else + interval = solo_enc->interval; + + /* Standard encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); + + /* Extended encoding only */ + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); + solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); + + /* Enables the standard encoder */ + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); + + /* Settle down Beavis... */ + mdelay(10); + + return 0; +} + +static void solo_enc_off(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (!fh->enc_on) + return; + + if (fh->kthread) { + kthread_stop(fh->kthread); + fh->kthread = NULL; + } + + solo_dev->enc_bw_remain += solo_enc->bw_weight; + fh->enc_on = 0; + + if (atomic_dec_return(&solo_enc->readers) > 0) + return; + + solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); + solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); +} + +static int solo_start_fh_thread(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + + fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6X10_NAME "_enc"); + + /* Oops, we had a problem */ + if (IS_ERR(fh->kthread)) { + spin_lock(&solo_enc->lock); + solo_enc_off(fh); + spin_unlock(&solo_enc->lock); + + return PTR_ERR(fh->kthread); + } + + return 0; +} + +static void enc_reset_gop(struct solo_dev *solo_dev, u8 ch) +{ + BUG_ON(ch >= solo_dev->nr_chans); + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1); + solo_dev->v4l2_enc[ch]->reset_gop = 1; +} + +static int enc_gop_reset(struct solo_dev *solo_dev, u8 ch, u8 vop) +{ + BUG_ON(ch >= solo_dev->nr_chans); + if (!solo_dev->v4l2_enc[ch]->reset_gop) + return 0; + if (vop) + return 1; + solo_dev->v4l2_enc[ch]->reset_gop = 0; + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), + solo_dev->v4l2_enc[ch]->gop); + return 0; +} + +static void enc_write_sg(struct scatterlist *sglist, void *buf, int size) +{ + struct scatterlist *sg; + u8 *src = buf; + + for (sg = sglist; sg && size > 0; sg = sg_next(sg)) { + u8 *p = sg_virt(sg); + size_t len = sg_dma_len(sg); + int i; + + for (i = 0; i < len && size; i++) + p[i] = *(src++); + } +} + +static int enc_get_mpeg_dma_sg(struct solo_dev *solo_dev, + struct p2m_desc *desc, + struct scatterlist *sglist, int skip, + unsigned int off, unsigned int size) +{ + int ret; + + if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) + return -EINVAL; + + if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, + desc, 0, sglist, skip, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, + sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off, + SOLO_MP4E_EXT_SIZE(solo_dev) - off); + + ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, + sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off, + SOLO_MP4E_EXT_ADDR(solo_dev), + size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); + + return ret; +} + +static int enc_get_mpeg_dma_t(struct solo_dev *solo_dev, + dma_addr_t buf, unsigned int off, + unsigned int size) +{ + int ret; + + if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) + return -EINVAL; + + if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, + SOLO_MP4E_EXT_ADDR(solo_dev) + off, + SOLO_MP4E_EXT_SIZE(solo_dev) - off); + + ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, + buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off, + SOLO_MP4E_EXT_ADDR(solo_dev), + size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); + + return ret; +} + +static int enc_get_mpeg_dma(struct solo_dev *solo_dev, void *buf, + unsigned int off, unsigned int size) +{ + int ret; + + dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size, + PCI_DMA_FROMDEVICE); + ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size); + pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE); + + return ret; +} + +static int enc_get_jpeg_dma_sg(struct solo_dev *solo_dev, + struct p2m_desc *desc, + struct scatterlist *sglist, int skip, + unsigned int off, unsigned int size) +{ + int ret; + + if (off > SOLO_JPEG_EXT_SIZE(solo_dev)) + return -EINVAL; + + if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) { + return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, + desc, 0, sglist, skip, + SOLO_JPEG_EXT_ADDR(solo_dev) + off, size); + } + + /* Buffer wrap */ + ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, + sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off, + SOLO_JPEG_EXT_SIZE(solo_dev) - off); + + ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, + sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off, + SOLO_JPEG_EXT_ADDR(solo_dev), + size + off - SOLO_JPEG_EXT_SIZE(solo_dev)); + + return ret; +} + +/* Returns true of __chk is within the first __range bytes of __off */ +#define OFF_IN_RANGE(__off, __range, __chk) \ + ((__off <= __chk) && ((__off + __range) >= __chk)) + +static void solo_jpeg_header(struct solo_enc_dev *solo_enc, + struct videobuf_dmabuf *vbuf) +{ + struct scatterlist *sg; + void *src = jpeg_header; + size_t copied = 0; + size_t to_copy = sizeof(jpeg_header); + + for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) { + size_t this_copy = min(sg_dma_len(sg), + (unsigned int)(to_copy - copied)); + u8 *p = sg_virt(sg); + + memcpy(p, src + copied, this_copy); + + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5)) + p[(SOF0_START + 5) - copied] = + 0xff & (solo_enc->height >> 8); + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6)) + p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height; + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7)) + p[(SOF0_START + 7) - copied] = + 0xff & (solo_enc->width >> 8); + if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8)) + p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width; + + copied += this_copy; + } +} + +static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, + struct videobuf_buffer *vb, + struct videobuf_dmabuf *vbuf) +{ + struct solo_dev *solo_dev = fh->enc->solo_dev; + int size = enc_buf->jpeg_size; + + /* Copy the header first (direct write) */ + solo_jpeg_header(fh->enc, vbuf); + + vb->size = size + sizeof(jpeg_header); + + /* Grab the jpeg frame */ + return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, + sizeof(jpeg_header), + enc_buf->jpeg_off, size); +} + +static inline int vop_interlaced(__le32 *vh) +{ + return (__le32_to_cpu(vh[0]) >> 30) & 1; +} + +static inline u32 vop_size(__le32 *vh) +{ + return __le32_to_cpu(vh[0]) & 0xFFFFF; +} + +static inline u8 vop_hsize(__le32 *vh) +{ + return (__le32_to_cpu(vh[1]) >> 8) & 0xFF; +} + +static inline u8 vop_vsize(__le32 *vh) +{ + return __le32_to_cpu(vh[1]) & 0xFF; +} + +/* must be called with *bits % 8 = 0 */ +static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count) +{ + memcpy(*out, src, count); + *out += count; + *bits += count * 8; +} + +static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count) +{ + + value <<= 32 - count; // shift to the right + + while (count--) { + **out <<= 1; + **out |= !!(value & (1 << 31)); /* MSB */ + value <<= 1; + if (++(*bits) % 8 == 0) + (*out)++; + } +} + +static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */ +{ + uint32_t max = 0, cnt = 0; + + while (value > max) { + max = (max + 2) * 2 - 2; + cnt++; + } + write_bits(out, bits, 1, cnt + 1); + write_bits(out, bits, ~(max - value), cnt); +} + +static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */ +{ + if (value <= 0) + write_ue(out, bits, -value * 2); + else + write_ue(out, bits, value * 2 - 1); +} + +static void write_mpeg4_end(u8 **out, unsigned *bits) +{ + write_bits(out, bits, 0, 1); + /* align on 32-bit boundary */ + if (*bits % 32) + write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32); +} + +static void write_h264_end(u8 **out, unsigned *bits, int align) +{ + write_bits(out, bits, 1, 1); + while ((*bits) % 8) + write_bits(out, bits, 0, 1); + if (align) + while ((*bits) % 32) + write_bits(out, bits, 0, 1); +} + +static void mpeg4_write_vol(u8 **out, struct solo_dev *solo_dev, + __le32 *vh, unsigned fps, unsigned interval) +{ + static const u8 hdr[] = { + 0, 0, 1, 0x00 /* video_object_start_code */, + 0, 0, 1, 0x20 /* video_object_layer_start_code */ + }; + unsigned bits = 0; + unsigned width = vop_hsize(vh) << 4; + unsigned height = vop_vsize(vh) << 4; + unsigned interlaced = vop_interlaced(vh); + + write_bytes(out, &bits, hdr, sizeof(hdr)); + write_bits(out, &bits, 0, 1); /* random_accessible_vol */ + write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */ + write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */ + write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */ + write_bits(out, &bits, 0, 3); /* video_object_layer_priority */ + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */ + else + write_bits(out, &bits, 2, 4); + write_bits(out, &bits, 1, 1); /* vol_control_parameters */ + write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */ + write_bits(out, &bits, 1, 1); /* low_delay */ + write_bits(out, &bits, 0, 1); /* vbv_parameters */ + write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, 1, 1); /* fixed_vop_rate */ + write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, width, 13); /* video_object_layer_width */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, height, 13); /* video_object_layer_height */ + write_bits(out, &bits, 1, 1); /* marker_bit */ + write_bits(out, &bits, interlaced, 1); /* interlaced */ + write_bits(out, &bits, 1, 1); /* obmc_disable */ + write_bits(out, &bits, 0, 2); /* sprite_enable */ + write_bits(out, &bits, 0, 1); /* not_8_bit */ + write_bits(out, &bits, 1, 0); /* quant_type */ + write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */ + write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */ + write_bits(out, &bits, 0, 1); /* quarter_sample */ + write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */ + write_bits(out, &bits, 1, 1); /* resync_marker_disable */ + write_bits(out, &bits, 0, 1); /* data_partitioned */ + write_bits(out, &bits, 0, 1); /* newpred_enable */ + write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */ + write_bits(out, &bits, 0, 1); /* scalability */ + write_mpeg4_end(out, &bits); +} + +static void h264_write_vol(u8 **out, struct solo_dev *solo_dev, __le32 *vh) +{ + static const u8 sps[] = { + 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */, + 0 /* constraints */, 30 /* level_idc */ + }; + static const u8 pps[] = { + 0, 0, 0, 1 /* start code */, 0x68 + }; + + unsigned bits = 0; + unsigned mbs_w = vop_hsize(vh); + unsigned mbs_h = vop_vsize(vh); + + write_bytes(out, &bits, sps, sizeof(sps)); + write_ue(out, &bits, 0); /* seq_parameter_set_id */ + write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */ + write_ue(out, &bits, 0); /* pic_order_cnt_type */ + write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */ + write_ue(out, &bits, 1); /* max_num_ref_frames */ + write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */ + write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */ + write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */ + write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */ + write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */ + write_bits(out, &bits, 0, 1); /* frame_cropping_flag */ + write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */ + write_h264_end(out, &bits, 0); + + write_bytes(out, &bits, pps, sizeof(pps)); + write_ue(out, &bits, 0); /* pic_parameter_set_id */ + write_ue(out, &bits, 0); /* seq_parameter_set_id */ + write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */ + write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */ + write_ue(out, &bits, 0); /* num_slice_groups_minus1 */ + write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */ + write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */ + write_bits(out, &bits, 0, 1); /* weighted_pred_flag */ + write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */ + write_se(out, &bits, 0); /* pic_init_qp_minus26 */ + write_se(out, &bits, 0); /* pic_init_qs_minus26 */ + write_se(out, &bits, 2); /* chroma_qp_index_offset */ + write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */ + write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */ + write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */ + write_h264_end(out, &bits, 1); +} + +static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, + struct videobuf_buffer *vb, + struct videobuf_dmabuf *vbuf) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + +#define VH_WORDS 16 +#define MAX_VOL_HEADER_LENGTH 64 + + __le32 vh[VH_WORDS]; + int ret; + int frame_size, frame_off; + int skip = 0; + + if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh))) + return -EINVAL; + + /* First get the hardware vop header (not real mpeg) */ + ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh)); + if (WARN_ON_ONCE(ret)) + return ret; + + if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size)) + return -EINVAL; + + vb->width = vop_hsize(vh) << 4; + vb->height = vop_vsize(vh) << 4; + vb->size = vop_size(vh); + + /* If this is a key frame, add extra m4v header */ + if (!enc_buf->vop) { + u8 header[MAX_VOL_HEADER_LENGTH], *out = header; + + if (solo_dev->flags & FLAGS_6110) + h264_write_vol(&out, solo_dev, vh); + else + mpeg4_write_vol(&out, solo_dev, vh, + solo_dev->fps * 1000, + solo_enc->interval * 1000); + skip = out - header; + enc_write_sg(vbuf->sglist, header, skip); + /* Adjust the dma buffer past this header */ + vb->size += skip; + } + + /* Now get the actual mpeg payload */ + frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); + frame_size = enc_buf->size - sizeof(vh); + + ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, + skip, frame_off, frame_size); + WARN_ON_ONCE(ret); + + return ret; +} + +static void solo_enc_fillbuf(struct solo_enc_fh *fh, + struct videobuf_buffer *vb) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct solo_enc_buf *enc_buf = NULL; + struct videobuf_dmabuf *vbuf; + int ret; + int error = 1; + u16 idx = fh->rd_idx; + + while (idx != solo_dev->enc_wr_idx) { + struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx]; + + idx = (idx + 1) % SOLO_NR_RING_BUFS; + + if (ebuf->ch != solo_enc->ch) + continue; + + if (fh->fmt == V4L2_PIX_FMT_MPEG) { + if (fh->type == ebuf->type) { + enc_buf = ebuf; + break; + } + } else { + /* For mjpeg, keep reading to the newest frame */ + enc_buf = ebuf; + } + } + + fh->rd_idx = idx; + + if (WARN_ON_ONCE(!enc_buf)) + goto buf_err; + + if ((fh->fmt == V4L2_PIX_FMT_MPEG && + vb->bsize < enc_buf->size) || + (fh->fmt == V4L2_PIX_FMT_MJPEG && + vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) { + WARN_ON_ONCE(1); + goto buf_err; + } + + vbuf = videobuf_to_dma(vb); + if (WARN_ON_ONCE(!vbuf)) + goto buf_err; + + if (fh->fmt == V4L2_PIX_FMT_MPEG) + ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf); + else + ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf); + + if (!ret) + error = 0; + +buf_err: + if (error) { + vb->state = VIDEOBUF_ERROR; + } else { + vb->field_count++; + vb->ts = enc_buf->ts; + vb->state = VIDEOBUF_DONE; + } + + wake_up(&vb->done); + + return; +} + +static void solo_enc_thread_try(struct solo_enc_fh *fh) +{ + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct videobuf_buffer *vb; + + for (;;) { + spin_lock(&solo_enc->lock); + + if (fh->rd_idx == solo_dev->enc_wr_idx) + break; + + if (list_empty(&fh->vidq_active)) + break; + + vb = list_first_entry(&fh->vidq_active, + struct videobuf_buffer, queue); + + if (!waitqueue_active(&vb->done)) + break; + + list_del(&vb->queue); + + spin_unlock(&solo_enc->lock); + + solo_enc_fillbuf(fh, vb); + } + + assert_spin_locked(&solo_enc->lock); + spin_unlock(&solo_enc->lock); +} + +static int solo_enc_thread(void *data) +{ + struct solo_enc_fh *fh = data; + struct solo_enc_dev *solo_enc = fh->enc; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_enc->thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_enc_thread_try(fh); + try_to_freeze(); + } + + remove_wait_queue(&solo_enc->thread_wait, &wait); + + return 0; +} + +void solo_motion_isr(struct solo_dev *solo_dev) +{ + u32 status; + int i; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION); + + status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS); + + for (i = 0; i < solo_dev->nr_chans; i++) { + struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i]; + + BUG_ON(solo_enc == NULL); + + if (solo_enc->motion_detected) + continue; + if (!(status & (1 << i))) + continue; + + solo_enc->motion_detected = 1; + } +} + +void solo_enc_v4l2_isr(struct solo_dev *solo_dev) +{ + struct solo_enc_buf *enc_buf; + u32 mpeg_current, mpeg_next, mpeg_size; + u32 jpeg_current, jpeg_next, jpeg_size; + u32 reg_mpeg_size; + u8 cur_q, vop_type; + u8 ch; + enum solo_enc_types enc_type; + + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER); + + cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS; + + reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7; + + while (solo_dev->enc_idx != cur_q) { + mpeg_current = solo_reg_read(solo_dev, + SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); + jpeg_current = solo_reg_read(solo_dev, + SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); + solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; + mpeg_next = solo_reg_read(solo_dev, + SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); + jpeg_next = solo_reg_read(solo_dev, + SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); + + ch = (mpeg_current >> 24) & 0x1f; + if (ch >= SOLO_MAX_CHANNELS) { + ch -= SOLO_MAX_CHANNELS; + enc_type = SOLO_ENC_TYPE_EXT; + } else + enc_type = SOLO_ENC_TYPE_STD; + + vop_type = (mpeg_current >> 29) & 3; + + mpeg_current &= 0x00ffffff; + mpeg_next &= 0x00ffffff; + jpeg_current &= 0x00ffffff; + jpeg_next &= 0x00ffffff; + + mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) + + mpeg_next - mpeg_current) % + SOLO_MP4E_EXT_SIZE(solo_dev); + + jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) + + jpeg_next - jpeg_current) % + SOLO_JPEG_EXT_SIZE(solo_dev); + + /* XXX I think this means we had a ring overflow? */ + if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) { + enc_reset_gop(solo_dev, ch); + continue; + } + + /* When resetting the GOP, skip frames until I-frame */ + if (enc_gop_reset(solo_dev, ch, vop_type)) + continue; + + enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx]; + + enc_buf->vop = vop_type; + enc_buf->ch = ch; + enc_buf->off = mpeg_current; + enc_buf->size = mpeg_size; + enc_buf->jpeg_off = jpeg_current; + enc_buf->jpeg_size = jpeg_size; + enc_buf->type = enc_type; + + do_gettimeofday(&enc_buf->ts); + + solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) % + SOLO_NR_RING_BUFS; + + wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait); + } + + return; +} + +static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + *size = FRAME_BUF_SIZE; + + if (*count < MIN_VID_BUFFERS) + *count = MIN_VID_BUFFERS; + + return 0; +} + +static int solo_enc_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct solo_enc_fh *fh = vq->priv_data; + struct solo_enc_dev *solo_enc = fh->enc; + + vb->size = FRAME_BUF_SIZE; + if (vb->baddr != 0 && vb->bsize < vb->size) + return -EINVAL; + + /* These properties only change when queue is idle */ + vb->width = solo_enc->width; + vb->height = solo_enc->height; + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + int rc = videobuf_iolock(vq, vb, NULL); + if (rc < 0) { + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; + return rc; + } + } + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void solo_enc_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct solo_enc_fh *fh = vq->priv_data; + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &fh->vidq_active); + wake_up_interruptible(&fh->enc->thread_wait); +} + +static void solo_enc_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops solo_enc_video_qops = { + .buf_setup = solo_enc_buf_setup, + .buf_prepare = solo_enc_buf_prepare, + .buf_queue = solo_enc_buf_queue, + .buf_release = solo_enc_buf_release, +}; + +static unsigned int solo_enc_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct solo_enc_fh *fh = file->private_data; + + return videobuf_poll_stream(file, &fh->vidq, wait); +} + +static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo_enc_fh *fh = file->private_data; + + return videobuf_mmap_mapper(&fh->vidq, vma); +} + +static int solo_enc_open(struct file *file) +{ + struct solo_enc_dev *solo_enc = video_drvdata(file); + struct solo_enc_fh *fh; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (fh == NULL) + return -ENOMEM; + + fh->enc = solo_enc; + file->private_data = fh; + INIT_LIST_HEAD(&fh->vidq_active); + fh->fmt = V4L2_PIX_FMT_MPEG; + fh->type = SOLO_ENC_TYPE_STD; + + videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops, + &solo_enc->solo_dev->pdev->dev, + &solo_enc->lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct videobuf_buffer), fh, NULL); + + return 0; +} + +static ssize_t solo_enc_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct solo_enc_fh *fh = file->private_data; + struct solo_enc_dev *solo_enc = fh->enc; + + /* Make sure the encoder is on */ + if (!fh->enc_on) { + int ret; + + spin_lock(&solo_enc->lock); + ret = solo_enc_on(fh); + spin_unlock(&solo_enc->lock); + if (ret) + return ret; + + ret = solo_start_fh_thread(fh); + if (ret) + return ret; + } + + return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static int solo_enc_release(struct file *file) +{ + struct solo_enc_fh *fh = file->private_data; + struct solo_enc_dev *solo_enc = fh->enc; + + videobuf_stop(&fh->vidq); + videobuf_mmap_free(&fh->vidq); + + spin_lock(&solo_enc->lock); + solo_enc_off(fh); + spin_unlock(&solo_enc->lock); + + kfree(fh); + + return 0; +} + +static int solo_enc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + + strcpy(cap->driver, SOLO6X10_NAME); + snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", + solo_enc->ch); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", + pci_name(solo_dev->pdev)); + cap->version = SOLO6X10_VER_NUM; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; +} + +static int solo_enc_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + + if (input->index) + return -EINVAL; + + snprintf(input->name, sizeof(input->name), "Encoder %d", + solo_enc->ch + 1); + input->type = V4L2_INPUT_TYPE_CAMERA; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + input->std = V4L2_STD_NTSC_M; + else + input->std = V4L2_STD_PAL_B; + + if (!tw28_get_video_status(solo_dev, solo_enc->ch)) + input->status = V4L2_IN_ST_NO_SIGNAL; + + return 0; +} + +static int solo_enc_set_input(struct file *file, void *priv, unsigned int index) +{ + if (index) + return -EINVAL; + + return 0; +} + +static int solo_enc_get_input(struct file *file, void *priv, + unsigned int *index) +{ + *index = 0; + + return 0; +} + +static int solo_enc_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + switch (f->index) { + case 0: + f->pixelformat = V4L2_PIX_FMT_MPEG; + strcpy(f->description, "MPEG-4 AVC"); + break; + case 1: + f->pixelformat = V4L2_PIX_FMT_MJPEG; + strcpy(f->description, "MJPEG"); + break; + default: + return -EINVAL; + } + + f->flags = V4L2_FMT_FLAG_COMPRESSED; + + return 0; +} + +static int solo_enc_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + + if (pix->pixelformat != V4L2_PIX_FMT_MPEG && + pix->pixelformat != V4L2_PIX_FMT_MJPEG) + return -EINVAL; + + /* We cannot change width/height in mid read */ + if (atomic_read(&solo_enc->readers) > 0) { + if (pix->width != solo_enc->width || + pix->height != solo_enc->height) + return -EBUSY; + } + + if (pix->width < solo_dev->video_hsize || + pix->height < solo_dev->video_vsize << 1) { + /* Default to CIF 1/2 size */ + pix->width = solo_dev->video_hsize >> 1; + pix->height = solo_dev->video_vsize; + } else { + /* Full frame */ + pix->width = solo_dev->video_hsize; + pix->height = solo_dev->video_vsize << 1; + } + + if (pix->field == V4L2_FIELD_ANY) + pix->field = V4L2_FIELD_INTERLACED; + else if (pix->field != V4L2_FIELD_INTERLACED) + pix->field = V4L2_FIELD_INTERLACED; + + /* Just set these */ + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->sizeimage = FRAME_BUF_SIZE; + + return 0; +} + +static int solo_enc_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + int ret; + + spin_lock(&solo_enc->lock); + + ret = solo_enc_try_fmt_cap(file, priv, f); + if (ret) { + spin_unlock(&solo_enc->lock); + return ret; + } + + if (pix->width == solo_dev->video_hsize) + solo_enc->mode = SOLO_ENC_MODE_D1; + else + solo_enc->mode = SOLO_ENC_MODE_CIF; + + /* This does not change the encoder at all */ + fh->fmt = pix->pixelformat; + + if (pix->priv) + fh->type = SOLO_ENC_TYPE_EXT; + ret = solo_enc_on(fh); + + spin_unlock(&solo_enc->lock); + + if (ret) + return ret; + + return solo_start_fh_thread(fh); +} + +static int solo_enc_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_enc->width; + pix->height = solo_enc->height; + pix->pixelformat = fh->fmt; + pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : + V4L2_FIELD_NONE; + pix->sizeimage = FRAME_BUF_SIZE; + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + + return 0; +} + +static int solo_enc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct solo_enc_fh *fh = priv; + + return videobuf_reqbufs(&fh->vidq, req); +} + +static int solo_enc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct solo_enc_fh *fh = priv; + + return videobuf_querybuf(&fh->vidq, buf); +} + +static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_enc_fh *fh = priv; + + return videobuf_qbuf(&fh->vidq, buf); +} + +static int solo_enc_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *buf) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + int ret; + + /* Make sure the encoder is on */ + if (!fh->enc_on) { + spin_lock(&solo_enc->lock); + ret = solo_enc_on(fh); + spin_unlock(&solo_enc->lock); + if (ret) + return ret; + + ret = solo_start_fh_thread(fh); + if (ret) + return ret; + } + + ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); + if (ret) + return ret; + + /* Signal motion detection */ + if (solo_is_motion_on(solo_enc)) { + buf->flags |= V4L2_BUF_FLAG_MOTION_ON; + if (solo_enc->motion_detected) { + buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED; + solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, + 1 << solo_enc->ch); + solo_enc->motion_detected = 0; + } + } + + /* Check for key frame on mpeg data */ + if (fh->fmt == V4L2_PIX_FMT_MPEG) { + struct videobuf_dmabuf *vbuf = + videobuf_to_dma(fh->vidq.bufs[buf->index]); + + if (vbuf) { + u8 *p = sg_virt(vbuf->sglist); + if (p[3] == 0x00) + buf->flags |= V4L2_BUF_FLAG_KEYFRAME; + else + buf->flags |= V4L2_BUF_FLAG_PFRAME; + } + } + + return 0; +} + +static int solo_enc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct solo_enc_fh *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamon(&fh->vidq); +} + +static int solo_enc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i) +{ + struct solo_enc_fh *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamoff(&fh->vidq); +} + +static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + return 0; +} + +static int solo_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct solo_enc_fh *fh = priv; + struct solo_dev *solo_dev = fh->enc->solo_dev; + + if (fsize->pixel_format != V4L2_PIX_FMT_MPEG) + return -EINVAL; + + switch (fsize->index) { + case 0: + fsize->discrete.width = solo_dev->video_hsize >> 1; + fsize->discrete.height = solo_dev->video_vsize; + break; + case 1: + fsize->discrete.width = solo_dev->video_hsize; + fsize->discrete.height = solo_dev->video_vsize << 1; + break; + default: + return -EINVAL; + } + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + + return 0; +} + +static int solo_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fintv) +{ + struct solo_enc_fh *fh = priv; + struct solo_dev *solo_dev = fh->enc->solo_dev; + + if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index) + return -EINVAL; + + fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; + + fintv->stepwise.min.numerator = solo_dev->fps; + fintv->stepwise.min.denominator = 1; + + fintv->stepwise.max.numerator = solo_dev->fps; + fintv->stepwise.max.denominator = 15; + + fintv->stepwise.step.numerator = 1; + fintv->stepwise.step.denominator = 1; + + return 0; +} + +static int solo_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = solo_enc->interval; + cp->timeperframe.denominator = solo_dev->fps; + cp->capturemode = 0; + /* XXX: Shouldn't we be able to get/set this from videobuf? */ + cp->readbuffers = 2; + + return 0; +} + +static int solo_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + struct v4l2_captureparm *cp = &sp->parm.capture; + + spin_lock(&solo_enc->lock); + + if (atomic_read(&solo_enc->readers) > 0) { + spin_unlock(&solo_enc->lock); + return -EBUSY; + } + + if ((cp->timeperframe.numerator == 0) || + (cp->timeperframe.denominator == 0)) { + /* reset framerate */ + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = solo_dev->fps; + } + + if (cp->timeperframe.denominator != solo_dev->fps) + cp->timeperframe.denominator = solo_dev->fps; + + if (cp->timeperframe.numerator > 15) + cp->timeperframe.numerator = 15; + + solo_enc->interval = cp->timeperframe.numerator; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + + solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1); + solo_update_mode(solo_enc); + + spin_unlock(&solo_enc->lock); + + return 0; +} + +static int solo_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + + qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); + if (!qc->id) + return -EINVAL; + + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80); + case V4L2_CID_SHARPNESS: + return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00); + case V4L2_CID_MPEG_VIDEO_ENCODING: + return v4l2_ctrl_query_fill( + qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, + V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps); +#ifdef PRIVATE_CIDS + case V4L2_CID_MOTION_THRESHOLD: + qc->flags |= V4L2_CTRL_FLAG_SLIDER; + qc->type = V4L2_CTRL_TYPE_INTEGER; + qc->minimum = 0; + qc->maximum = 0xffff; + qc->step = 1; + qc->default_value = SOLO_DEF_MOT_THRESH; + strlcpy(qc->name, "Motion Detection Threshold", + sizeof(qc->name)); + return 0; + case V4L2_CID_MOTION_ENABLE: + qc->type = V4L2_CTRL_TYPE_BOOLEAN; + qc->minimum = 0; + qc->maximum = qc->step = 1; + qc->default_value = 0; + strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name)); + return 0; +#else + case V4L2_CID_MOTION_THRESHOLD: + return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1, + SOLO_DEF_MOT_THRESH); + case V4L2_CID_MOTION_ENABLE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); +#endif + case V4L2_CID_RDS_TX_RADIO_TEXT: + qc->type = V4L2_CTRL_TYPE_STRING; + qc->minimum = 0; + qc->maximum = OSD_TEXT_MAX; + qc->step = 1; + qc->default_value = 0; + strlcpy(qc->name, "OSD Text", sizeof(qc->name)); + return 0; + } + + return -EINVAL; +} + +static int solo_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + struct v4l2_queryctrl qctrl; + int err; + + qctrl.id = qmenu->id; + err = solo_queryctrl(file, priv, &qctrl); + if (err) + return err; + + return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); +} + +static int solo_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_SHARPNESS: + return tw28_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, + &ctrl->value); + case V4L2_CID_MPEG_VIDEO_ENCODING: + ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + ctrl->value = solo_enc->gop; + break; + case V4L2_CID_MOTION_THRESHOLD: + ctrl->value = solo_enc->motion_thresh; + break; + case V4L2_CID_MOTION_ENABLE: + ctrl->value = solo_is_motion_on(solo_enc); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int solo_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + struct solo_dev *solo_dev = solo_enc->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + case V4L2_CID_CONTRAST: + case V4L2_CID_SATURATION: + case V4L2_CID_HUE: + case V4L2_CID_SHARPNESS: + return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, + ctrl->value); + case V4L2_CID_MPEG_VIDEO_ENCODING: + if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) + return -ERANGE; + break; + case V4L2_CID_MPEG_VIDEO_GOP_SIZE: + if (ctrl->value < 1 || ctrl->value > 255) + return -ERANGE; + solo_enc->gop = ctrl->value; + solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), + solo_enc->gop); + solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), + solo_enc->gop); + break; + case V4L2_CID_MOTION_THRESHOLD: + /* TODO accept value on lower 16-bits and use high + * 16-bits to assign the value to a specific block */ + if (ctrl->value < 0 || ctrl->value > 0xffff) + return -ERANGE; + solo_enc->motion_thresh = ctrl->value; + solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value); + break; + case V4L2_CID_MOTION_ENABLE: + solo_motion_toggle(solo_enc, ctrl->value); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int solo_s_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + int i; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = (ctrls->controls + i); + int err; + + switch (ctrl->id) { + case V4L2_CID_RDS_TX_RADIO_TEXT: + if (ctrl->size - 1 > OSD_TEXT_MAX) + err = -ERANGE; + else { + err = copy_from_user(solo_enc->osd_text, + ctrl->string, + OSD_TEXT_MAX); + solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; + if (!err) + err = solo_osd_print(solo_enc); + } + break; + default: + err = -EINVAL; + } + + if (err < 0) { + ctrls->error_idx = i; + return err; + } + } + + return 0; +} + +static int solo_g_ext_ctrls(struct file *file, void *priv, + struct v4l2_ext_controls *ctrls) +{ + struct solo_enc_fh *fh = priv; + struct solo_enc_dev *solo_enc = fh->enc; + int i; + + for (i = 0; i < ctrls->count; i++) { + struct v4l2_ext_control *ctrl = (ctrls->controls + i); + int err; + + switch (ctrl->id) { + case V4L2_CID_RDS_TX_RADIO_TEXT: + if (ctrl->size < OSD_TEXT_MAX) { + ctrl->size = OSD_TEXT_MAX; + err = -ENOSPC; + } else { + err = copy_to_user(ctrl->string, + solo_enc->osd_text, + OSD_TEXT_MAX); + } + break; + default: + err = -EINVAL; + } + + if (err < 0) { + ctrls->error_idx = i; + return err; + } + } + + return 0; +} + +static const struct v4l2_file_operations solo_enc_fops = { + .owner = THIS_MODULE, + .open = solo_enc_open, + .release = solo_enc_release, + .read = solo_enc_read, + .poll = solo_enc_poll, + .mmap = solo_enc_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { + .vidioc_querycap = solo_enc_querycap, + .vidioc_s_std = solo_enc_s_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enc_enum_input, + .vidioc_s_input = solo_enc_set_input, + .vidioc_g_input = solo_enc_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = solo_enc_reqbufs, + .vidioc_querybuf = solo_enc_querybuf, + .vidioc_qbuf = solo_enc_qbuf, + .vidioc_dqbuf = solo_enc_dqbuf, + .vidioc_streamon = solo_enc_streamon, + .vidioc_streamoff = solo_enc_streamoff, + /* Frame size and interval */ + .vidioc_enum_framesizes = solo_enum_framesizes, + .vidioc_enum_frameintervals = solo_enum_frameintervals, + /* Video capture parameters */ + .vidioc_s_parm = solo_s_parm, + .vidioc_g_parm = solo_g_parm, + /* Controls */ + .vidioc_queryctrl = solo_queryctrl, + .vidioc_querymenu = solo_querymenu, + .vidioc_g_ctrl = solo_g_ctrl, + .vidioc_s_ctrl = solo_s_ctrl, + .vidioc_g_ext_ctrls = solo_g_ext_ctrls, + .vidioc_s_ext_ctrls = solo_s_ext_ctrls, +}; + +static struct video_device solo_enc_template = { + .name = SOLO6X10_NAME, + .fops = &solo_enc_fops, + .ioctl_ops = &solo_enc_ioctl_ops, + .minor = -1, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, + .current_norm = V4L2_STD_NTSC_M, +}; + +static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch) +{ + struct solo_enc_dev *solo_enc; + int ret; + + solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); + if (!solo_enc) + return ERR_PTR(-ENOMEM); + + solo_enc->vfd = video_device_alloc(); + if (!solo_enc->vfd) { + kfree(solo_enc); + return ERR_PTR(-ENOMEM); + } + + solo_enc->solo_dev = solo_dev; + solo_enc->ch = ch; + + *solo_enc->vfd = solo_enc_template; + solo_enc->vfd->parent = &solo_dev->pdev->dev; + ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, + video_nr); + if (ret < 0) { + video_device_release(solo_enc->vfd); + kfree(solo_enc); + return ERR_PTR(ret); + } + + video_set_drvdata(solo_enc->vfd, solo_enc); + + snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), + "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, + solo_enc->vfd->num); + + if (video_nr != -1) + video_nr++; + + spin_lock_init(&solo_enc->lock); + init_waitqueue_head(&solo_enc->thread_wait); + atomic_set(&solo_enc->readers, 0); + + solo_enc->qp = SOLO_DEFAULT_QP; + solo_enc->gop = solo_dev->fps; + solo_enc->interval = 1; + solo_enc->mode = SOLO_ENC_MODE_CIF; + solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; + + spin_lock(&solo_enc->lock); + solo_update_mode(solo_enc); + spin_unlock(&solo_enc->lock); + + return solo_enc; +} + +static void solo_enc_free(struct solo_enc_dev *solo_enc) +{ + if (solo_enc == NULL) + return; + + video_unregister_device(solo_enc->vfd); + kfree(solo_enc); +} + +int solo_enc_v4l2_init(struct solo_dev *solo_dev) +{ + int i; + + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i); + if (IS_ERR(solo_dev->v4l2_enc[i])) + break; + } + + if (i != solo_dev->nr_chans) { + int ret = PTR_ERR(solo_dev->v4l2_enc[i]); + while (i--) + solo_enc_free(solo_dev->v4l2_enc[i]); + return ret; + } + + /* D1@MAX-FPS * 4 */ + solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; + + dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", + solo_dev->v4l2_enc[0]->vfd->num, + solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); + + return 0; +} + +void solo_enc_v4l2_exit(struct solo_dev *solo_dev) +{ + int i; + + solo_irq_off(solo_dev, SOLO_IRQ_MOTION); + + for (i = 0; i < solo_dev->nr_chans; i++) + solo_enc_free(solo_dev->v4l2_enc[i]); +} diff --git a/drivers/staging/media/solo6x10/v4l2.c b/drivers/staging/media/solo6x10/v4l2.c new file mode 100644 index 000000000000..571c3a348d30 --- /dev/null +++ b/drivers/staging/media/solo6x10/v4l2.c @@ -0,0 +1,964 @@ +/* + * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com + * Copyright (C) 2010 Ben Collins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public 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 "solo6x10.h" +#include "tw28.h" + +#define SOLO_HW_BPL 2048 +#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED + +/* Image size is two fields, SOLO_HW_BPL is one horizontal line */ +#define solo_vlines(__solo) (__solo->video_vsize * 2) +#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ + solo_vlines(__solo)) +#define solo_bytesperline(__solo) (__solo->video_hsize * 2) + +#define MIN_VID_BUFFERS 4 + +/* Simple file handle */ +struct solo_filehandle { + struct solo_dev *solo_dev; + struct videobuf_queue vidq; + struct task_struct *kthread; + spinlock_t slock; + int old_write; + struct list_head vidq_active; + struct p2m_desc desc[SOLO_NR_P2M_DESC]; + int desc_idx; +}; + +unsigned video_nr = -1; +module_param(video_nr, uint, 0644); +MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); + +static void erase_on(struct solo_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); + solo_dev->erasing = 1; + solo_dev->frame_blank = 0; +} + +static int erase_off(struct solo_dev *solo_dev) +{ + if (!solo_dev->erasing) + return 0; + + /* First time around, assert erase off */ + if (!solo_dev->frame_blank) + solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); + /* Keep the erasing flag on for 8 frames minimum */ + if (solo_dev->frame_blank++ >= 8) + solo_dev->erasing = 0; + + return 1; +} + +void solo_video_in_isr(struct solo_dev *solo_dev) +{ + solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN); + wake_up_interruptible(&solo_dev->disp_thread_wait); +} + +static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, + int sx, int sy, int ex, int ey, int scale) +{ + if (ch >= solo_dev->nr_chans) + return; + + /* Here, we just keep window/channel the same */ + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), + SOLO_VI_WIN_CHANNEL(ch) | + SOLO_VI_WIN_SX(sx) | + SOLO_VI_WIN_EX(ex) | + SOLO_VI_WIN_SCALE(scale)); + + solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), + SOLO_VI_WIN_SY(sy) | + SOLO_VI_WIN_EY(ey)); +} + +static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) +{ + u8 ch = idx * 4; + + if (ch >= solo_dev->nr_chans) + return -EINVAL; + + if (!on) { + u8 i; + for (i = ch; i < ch + 4; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + /* Row 1 */ + solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, 3); + solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, + solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); + /* Row 2 */ + solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, + solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); + solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, + solo_vlines(solo_dev) / 2, solo_dev->video_hsize, + solo_vlines(solo_dev), 3); + + return 0; +} + +static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) +{ + int sy, ysize, hsize, i; + + if (!on) { + for (i = 0; i < 16; i++) + solo_win_setup(solo_dev, i, solo_dev->video_hsize, + solo_vlines(solo_dev), + solo_dev->video_hsize, + solo_vlines(solo_dev), 0); + return 0; + } + + ysize = solo_vlines(solo_dev) / 4; + hsize = solo_dev->video_hsize / 4; + + for (sy = 0, i = 0; i < 4; i++, sy += ysize) { + solo_win_setup(solo_dev, i * 4, 0, sy, hsize, + sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, + hsize * 2, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, + hsize * 3, sy + ysize, 5); + solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, + solo_dev->video_hsize, sy + ysize, 5); + } + + return 0; +} + +static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) +{ + u8 ext_ch; + + if (ch < solo_dev->nr_chans) { + solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, + on ? 0 : solo_vlines(solo_dev), + solo_dev->video_hsize, solo_vlines(solo_dev), + on ? 1 : 0); + return 0; + } + + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + ext_ch = ch - solo_dev->nr_chans; + + /* 4up's first */ + if (ext_ch < 4) + return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); + + /* Remaining case is 16up for 16-port */ + return solo_v4l2_ch_ext_16up(solo_dev, on); +} + +static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) +{ + if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) + return -EINVAL; + + erase_on(solo_dev); + + solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); + solo_v4l2_ch(solo_dev, ch, 1); + + solo_dev->cur_disp_ch = ch; + + return 0; +} + +static void disp_reset_desc(struct solo_filehandle *fh) +{ + /* We use desc mode, which ignores desc 0 */ + memset(fh->desc, 0, sizeof(*fh->desc)); + fh->desc_idx = 1; +} + +static int disp_flush_descs(struct solo_filehandle *fh) +{ + int ret; + + if (!fh->desc_idx) + return 0; + + ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP, + fh->desc, fh->desc_idx); + disp_reset_desc(fh); + + return ret; +} + +static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr, + u32 ext_addr, int size, int repeat, int ext_size) +{ + if (fh->desc_idx >= SOLO_NR_P2M_DESC) { + int ret = disp_flush_descs(fh); + if (ret) + return ret; + } + + solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr, + size, repeat, ext_size); + fh->desc_idx++; + + return 0; +} + +static void solo_fillbuf(struct solo_filehandle *fh, + struct videobuf_buffer *vb) +{ + struct solo_dev *solo_dev = fh->solo_dev; + struct videobuf_dmabuf *vbuf; + unsigned int fdma_addr; + int error = 1; + int i; + struct scatterlist *sg; + dma_addr_t sg_dma; + int sg_size_left; + + vbuf = videobuf_to_dma(vb); + if (!vbuf) + goto finish_buf; + + if (erase_off(solo_dev)) { + int i; + + /* Just blit to the entire sg list, ignoring size */ + for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) { + void *p = sg_virt(sg); + size_t len = sg_dma_len(sg); + + for (i = 0; i < len; i += 2) { + ((u8 *)p)[i] = 0x80; + ((u8 *)p)[i + 1] = 0x00; + } + } + + error = 0; + goto finish_buf; + } + + disp_reset_desc(fh); + sg = vbuf->sglist; + sg_dma = sg_dma_address(sg); + sg_size_left = sg_dma_len(sg); + + fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write * + (SOLO_HW_BPL * solo_vlines(solo_dev))); + + for (i = 0; i < solo_vlines(solo_dev); i++) { + int line_len = solo_bytesperline(solo_dev); + int lines; + + if (!sg_size_left) { + sg = sg_next(sg); + if (sg == NULL) + goto finish_buf; + sg_dma = sg_dma_address(sg); + sg_size_left = sg_dma_len(sg); + } + + /* No room for an entire line, so chunk it up */ + if (sg_size_left < line_len) { + int this_addr = fdma_addr; + + while (line_len > 0) { + int this_write; + + if (!sg_size_left) { + sg = sg_next(sg); + if (sg == NULL) + goto finish_buf; + sg_dma = sg_dma_address(sg); + sg_size_left = sg_dma_len(sg); + } + + this_write = min(sg_size_left, line_len); + + if (disp_push_desc(fh, sg_dma, this_addr, + this_write, 0, 0)) + goto finish_buf; + + line_len -= this_write; + sg_size_left -= this_write; + sg_dma += this_write; + this_addr += this_write; + } + + fdma_addr += SOLO_HW_BPL; + continue; + } + + /* Shove as many lines into a repeating descriptor as possible */ + lines = min(sg_size_left / line_len, + solo_vlines(solo_dev) - i); + + if (disp_push_desc(fh, sg_dma, fdma_addr, line_len, + lines - 1, SOLO_HW_BPL)) + goto finish_buf; + + i += lines - 1; + fdma_addr += SOLO_HW_BPL * lines; + sg_dma += lines * line_len; + sg_size_left -= lines * line_len; + } + + error = disp_flush_descs(fh); + +finish_buf: + if (error) { + vb->state = VIDEOBUF_ERROR; + } else { + vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev); + vb->state = VIDEOBUF_DONE; + vb->field_count++; + do_gettimeofday(&vb->ts); + } + + wake_up(&vb->done); + + return; +} + +static void solo_thread_try(struct solo_filehandle *fh) +{ + struct videobuf_buffer *vb; + unsigned int cur_write; + + for (;;) { + spin_lock(&fh->slock); + + if (list_empty(&fh->vidq_active)) + break; + + vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer, + queue); + + if (!waitqueue_active(&vb->done)) + break; + + cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev, + SOLO_VI_STATUS0)); + if (cur_write == fh->old_write) + break; + + fh->old_write = cur_write; + list_del(&vb->queue); + + spin_unlock(&fh->slock); + + solo_fillbuf(fh, vb); + } + + assert_spin_locked(&fh->slock); + spin_unlock(&fh->slock); +} + +static int solo_thread(void *data) +{ + struct solo_filehandle *fh = data; + struct solo_dev *solo_dev = fh->solo_dev; + DECLARE_WAITQUEUE(wait, current); + + set_freezable(); + add_wait_queue(&solo_dev->disp_thread_wait, &wait); + + for (;;) { + long timeout = schedule_timeout_interruptible(HZ); + if (timeout == -ERESTARTSYS || kthread_should_stop()) + break; + solo_thread_try(fh); + try_to_freeze(); + } + + remove_wait_queue(&solo_dev->disp_thread_wait, &wait); + + return 0; +} + +static int solo_start_thread(struct solo_filehandle *fh) +{ + fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp"); + + if (IS_ERR(fh->kthread)) + return PTR_ERR(fh->kthread); + + return 0; +} + +static void solo_stop_thread(struct solo_filehandle *fh) +{ + if (fh->kthread) { + kthread_stop(fh->kthread); + fh->kthread = NULL; + } +} + +static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct solo_filehandle *fh = vq->priv_data; + struct solo_dev *solo_dev = fh->solo_dev; + + *size = solo_image_size(solo_dev); + + if (*count < MIN_VID_BUFFERS) + *count = MIN_VID_BUFFERS; + + return 0; +} + +static int solo_buf_prepare(struct videobuf_queue *vq, + struct videobuf_buffer *vb, enum v4l2_field field) +{ + struct solo_filehandle *fh = vq->priv_data; + struct solo_dev *solo_dev = fh->solo_dev; + + vb->size = solo_image_size(solo_dev); + if (vb->baddr != 0 && vb->bsize < vb->size) + return -EINVAL; + + /* XXX: These properties only change when queue is idle */ + vb->width = solo_dev->video_hsize; + vb->height = solo_vlines(solo_dev); + vb->bytesperline = solo_bytesperline(solo_dev); + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + int rc = videobuf_iolock(vq, vb, NULL); + if (rc < 0) { + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; + return rc; + } + } + vb->state = VIDEOBUF_PREPARED; + + return 0; +} + +static void solo_buf_queue(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct solo_filehandle *fh = vq->priv_data; + struct solo_dev *solo_dev = fh->solo_dev; + + vb->state = VIDEOBUF_QUEUED; + list_add_tail(&vb->queue, &fh->vidq_active); + wake_up_interruptible(&solo_dev->disp_thread_wait); +} + +static void solo_buf_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(vb); + + videobuf_dma_unmap(vq->dev, dma); + videobuf_dma_free(dma); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops solo_video_qops = { + .buf_setup = solo_buf_setup, + .buf_prepare = solo_buf_prepare, + .buf_queue = solo_buf_queue, + .buf_release = solo_buf_release, +}; + +static unsigned int solo_v4l2_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct solo_filehandle *fh = file->private_data; + + return videobuf_poll_stream(file, &fh->vidq, wait); +} + +static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct solo_filehandle *fh = file->private_data; + + return videobuf_mmap_mapper(&fh->vidq, vma); +} + +static int solo_v4l2_open(struct file *file) +{ + struct solo_dev *solo_dev = video_drvdata(file); + struct solo_filehandle *fh; + int ret; + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (fh == NULL) + return -ENOMEM; + + spin_lock_init(&fh->slock); + INIT_LIST_HEAD(&fh->vidq_active); + fh->solo_dev = solo_dev; + file->private_data = fh; + + ret = solo_start_thread(fh); + if (ret) { + kfree(fh); + return ret; + } + + videobuf_queue_sg_init(&fh->vidq, &solo_video_qops, + &solo_dev->pdev->dev, &fh->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + SOLO_DISP_PIX_FIELD, + sizeof(struct videobuf_buffer), fh, NULL); + + return 0; +} + +static ssize_t solo_v4l2_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct solo_filehandle *fh = file->private_data; + + return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static int solo_v4l2_release(struct file *file) +{ + struct solo_filehandle *fh = file->private_data; + + videobuf_stop(&fh->vidq); + videobuf_mmap_free(&fh->vidq); + solo_stop_thread(fh); + kfree(fh); + + return 0; +} + +static int solo_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct solo_filehandle *fh = priv; + struct solo_dev *solo_dev = fh->solo_dev; + + strcpy(cap->driver, SOLO6X10_NAME); + strcpy(cap->card, "Softlogic 6x10"); + snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", + pci_name(solo_dev->pdev)); + cap->version = SOLO6X10_VER_NUM; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + return 0; +} + +static int solo_enum_ext_input(struct solo_dev *solo_dev, + struct v4l2_input *input) +{ + static const char *dispnames_1[] = { "4UP" }; + static const char *dispnames_2[] = { "4UP-1", "4UP-2" }; + static const char *dispnames_5[] = { + "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" + }; + const char **dispnames; + + if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) + return -EINVAL; + + if (solo_dev->nr_ext == 5) + dispnames = dispnames_5; + else if (solo_dev->nr_ext == 2) + dispnames = dispnames_2; + else + dispnames = dispnames_1; + + snprintf(input->name, sizeof(input->name), "Multi %s", + dispnames[input->index - solo_dev->nr_chans]); + + return 0; +} + +static int solo_enum_input(struct file *file, void *priv, + struct v4l2_input *input) +{ + struct solo_filehandle *fh = priv; + struct solo_dev *solo_dev = fh->solo_dev; + + if (input->index >= solo_dev->nr_chans) { + int ret = solo_enum_ext_input(solo_dev, input); + if (ret < 0) + return ret; + } else { + snprintf(input->name, sizeof(input->name), "Camera %d", + input->index + 1); + + /* We can only check this for normal inputs */ + if (!tw28_get_video_status(solo_dev, input->index)) + input->status = V4L2_IN_ST_NO_SIGNAL; + } + + input->type = V4L2_INPUT_TYPE_CAMERA; + + if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) + input->std = V4L2_STD_NTSC_M; + else + input->std = V4L2_STD_PAL_B; + + return 0; +} + +static int solo_set_input(struct file *file, void *priv, unsigned int index) +{ + struct solo_filehandle *fh = priv; + + return solo_v4l2_set_ch(fh->solo_dev, index); +} + +static int solo_get_input(struct file *file, void *priv, unsigned int *index) +{ + struct solo_filehandle *fh = priv; + + *index = fh->solo_dev->cur_disp_ch; + + return 0; +} + +static int solo_enum_fmt_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index) + return -EINVAL; + + f->pixelformat = V4L2_PIX_FMT_UYVY; + strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); + + return 0; +} + +static int solo_try_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_filehandle *fh = priv; + struct solo_dev *solo_dev = fh->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + int image_size = solo_image_size(solo_dev); + + /* Check supported sizes */ + if (pix->width != solo_dev->video_hsize) + pix->width = solo_dev->video_hsize; + if (pix->height != solo_vlines(solo_dev)) + pix->height = solo_vlines(solo_dev); + if (pix->sizeimage != image_size) + pix->sizeimage = image_size; + + /* Check formats */ + if (pix->field == V4L2_FIELD_ANY) + pix->field = SOLO_DISP_PIX_FIELD; + + if (pix->pixelformat != V4L2_PIX_FMT_UYVY || + pix->field != SOLO_DISP_PIX_FIELD || + pix->colorspace != V4L2_COLORSPACE_SMPTE170M) + return -EINVAL; + + return 0; +} + +static int solo_set_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_filehandle *fh = priv; + + if (videobuf_queue_is_busy(&fh->vidq)) + return -EBUSY; + + /* For right now, if it doesn't match our running config, + * then fail */ + return solo_try_fmt_cap(file, priv, f); +} + +static int solo_get_fmt_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct solo_filehandle *fh = priv; + struct solo_dev *solo_dev = fh->solo_dev; + struct v4l2_pix_format *pix = &f->fmt.pix; + + pix->width = solo_dev->video_hsize; + pix->height = solo_vlines(solo_dev); + pix->pixelformat = V4L2_PIX_FMT_UYVY; + pix->field = SOLO_DISP_PIX_FIELD; + pix->sizeimage = solo_image_size(solo_dev); + pix->colorspace = V4L2_COLORSPACE_SMPTE170M; + pix->bytesperline = solo_bytesperline(solo_dev); + + return 0; +} + +static int solo_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *req) +{ + struct solo_filehandle *fh = priv; + + return videobuf_reqbufs(&fh->vidq, req); +} + +static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_filehandle *fh = priv; + + return videobuf_querybuf(&fh->vidq, buf); +} + +static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_filehandle *fh = priv; + + return videobuf_qbuf(&fh->vidq, buf); +} + +static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) +{ + struct solo_filehandle *fh = priv; + + return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); +} + +static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct solo_filehandle *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamon(&fh->vidq); +} + +static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct solo_filehandle *fh = priv; + + if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamoff(&fh->vidq); +} + +static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + return 0; +} + +static const u32 solo_motion_ctrls[] = { + V4L2_CID_MOTION_TRACE, + 0 +}; + +static const u32 *solo_ctrl_classes[] = { + solo_motion_ctrls, + NULL +}; + +static int solo_disp_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); + if (!qc->id) + return -EINVAL; + + switch (qc->id) { +#ifdef PRIVATE_CIDS + case V4L2_CID_MOTION_TRACE: + qc->type = V4L2_CTRL_TYPE_BOOLEAN; + qc->minimum = 0; + qc->maximum = qc->step = 1; + qc->default_value = 0; + strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name)); + return 0; +#else + case V4L2_CID_MOTION_TRACE: + return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); +#endif + } + return -EINVAL; +} + +static int solo_disp_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_filehandle *fh = priv; + struct solo_dev *solo_dev = fh->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_MOTION_TRACE: + ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR) + ? 1 : 0; + return 0; + } + return -EINVAL; +} + +static int solo_disp_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct solo_filehandle *fh = priv; + struct solo_dev *solo_dev = fh->solo_dev; + + switch (ctrl->id) { + case V4L2_CID_MOTION_TRACE: + if (ctrl->value) { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, + SOLO_VI_MOTION_Y_ADD | + SOLO_VI_MOTION_Y_VALUE(0x20) | + SOLO_VI_MOTION_CB_VALUE(0x10) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, + SOLO_VI_MOTION_CR_ADD | + SOLO_VI_MOTION_Y_VALUE(0x10) | + SOLO_VI_MOTION_CB_VALUE(0x80) | + SOLO_VI_MOTION_CR_VALUE(0x10)); + } else { + solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); + solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); + } + return 0; + } + return -EINVAL; +} + +static const struct v4l2_file_operations solo_v4l2_fops = { + .owner = THIS_MODULE, + .open = solo_v4l2_open, + .release = solo_v4l2_release, + .read = solo_v4l2_read, + .poll = solo_v4l2_poll, + .mmap = solo_v4l2_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { + .vidioc_querycap = solo_querycap, + .vidioc_s_std = solo_s_std, + /* Input callbacks */ + .vidioc_enum_input = solo_enum_input, + .vidioc_s_input = solo_set_input, + .vidioc_g_input = solo_get_input, + /* Video capture format callbacks */ + .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, + .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, + .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, + .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, + /* Streaming I/O */ + .vidioc_reqbufs = solo_reqbufs, + .vidioc_querybuf = solo_querybuf, + .vidioc_qbuf = solo_qbuf, + .vidioc_dqbuf = solo_dqbuf, + .vidioc_streamon = solo_streamon, + .vidioc_streamoff = solo_streamoff, + /* Controls */ + .vidioc_queryctrl = solo_disp_queryctrl, + .vidioc_g_ctrl = solo_disp_g_ctrl, + .vidioc_s_ctrl = solo_disp_s_ctrl, +}; + +static struct video_device solo_v4l2_template = { + .name = SOLO6X10_NAME, + .fops = &solo_v4l2_fops, + .ioctl_ops = &solo_v4l2_ioctl_ops, + .minor = -1, + .release = video_device_release, + + .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, + .current_norm = V4L2_STD_NTSC_M, +}; + +int solo_v4l2_init(struct solo_dev *solo_dev) +{ + int ret; + int i; + + init_waitqueue_head(&solo_dev->disp_thread_wait); + + solo_dev->vfd = video_device_alloc(); + if (!solo_dev->vfd) + return -ENOMEM; + + *solo_dev->vfd = solo_v4l2_template; + solo_dev->vfd->parent = &solo_dev->pdev->dev; + + ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr); + if (ret < 0) { + video_device_release(solo_dev->vfd); + solo_dev->vfd = NULL; + return ret; + } + + video_set_drvdata(solo_dev->vfd, solo_dev); + + snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", + SOLO6X10_NAME, solo_dev->vfd->num); + + if (video_nr != -1) + video_nr++; + + dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " + "%d inputs (%d extended)\n", solo_dev->vfd->num, + solo_dev->nr_chans, solo_dev->nr_ext); + + /* Cycle all the channels and clear */ + for (i = 0; i < solo_dev->nr_chans; i++) { + solo_v4l2_set_ch(solo_dev, i); + while (erase_off(solo_dev)) + ;/* Do nothing */ + } + + /* Set the default display channel */ + solo_v4l2_set_ch(solo_dev, 0); + while (erase_off(solo_dev)) + ;/* Do nothing */ + + solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); + + return 0; +} + +void solo_v4l2_exit(struct solo_dev *solo_dev) +{ + solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); + if (solo_dev->vfd) { + video_unregister_device(solo_dev->vfd); + solo_dev->vfd = NULL; + } +} diff --git a/drivers/staging/solo6x10/Kconfig b/drivers/staging/solo6x10/Kconfig deleted file mode 100644 index 03dcac4ea4d0..000000000000 --- a/drivers/staging/solo6x10/Kconfig +++ /dev/null @@ -1,8 +0,0 @@ -config SOLO6X10 - tristate "Softlogic 6x10 MPEG codec cards" - depends on PCI && VIDEO_DEV && SND && I2C - select VIDEOBUF_DMA_SG - select SND_PCM - ---help--- - This driver supports the Softlogic based MPEG-4 and h.264 codec - codec cards. diff --git a/drivers/staging/solo6x10/Makefile b/drivers/staging/solo6x10/Makefile deleted file mode 100644 index 72816cf16704..000000000000 --- a/drivers/staging/solo6x10/Makefile +++ /dev/null @@ -1,3 +0,0 @@ -solo6x10-y := core.o i2c.o p2m.o v4l2.o tw28.o gpio.o disp.o enc.o v4l2-enc.o g723.o - -obj-$(CONFIG_SOLO6X10) := solo6x10.o diff --git a/drivers/staging/solo6x10/TODO b/drivers/staging/solo6x10/TODO deleted file mode 100644 index 7e6c4fa130df..000000000000 --- a/drivers/staging/solo6x10/TODO +++ /dev/null @@ -1,24 +0,0 @@ -TODO (staging => main): - - * Motion detection flags need to be moved to v4l2 - * Some private CIDs need to be moved to v4l2 - -TODO (general): - - * encoder on/off controls - * mpeg cid bitrate mode (vbr/cbr) - * mpeg cid bitrate/bitrate-peak - * mpeg encode of user data - * mpeg decode of user data - * switch between 4 frames/irq to 1 when using mjpeg (and then back - when not) - * implement a CID control for motion areas/thresholds - * implement CID controls for mozaic areas - * allow for higher level of interval (for < 1 fps) - * sound: - - implement playback via external sound jack - - implement loopback of external sound jack with incoming audio? - - implement pause/resume - -Plase send patches to Greg Kroah-Hartman and Cc Ben Collins - diff --git a/drivers/staging/solo6x10/core.c b/drivers/staging/solo6x10/core.c deleted file mode 100644 index f974f6412ad7..000000000000 --- a/drivers/staging/solo6x10/core.c +++ /dev/null @@ -1,332 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" -#include "tw28.h" - -MODULE_DESCRIPTION("Softlogic 6x10 MP4/H.264 Encoder/Decoder V4L2/ALSA Driver"); -MODULE_AUTHOR("Ben Collins "); -MODULE_VERSION(SOLO6X10_VERSION); -MODULE_LICENSE("GPL"); - -void solo_irq_on(struct solo_dev *solo_dev, u32 mask) -{ - solo_dev->irq_mask |= mask; - solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); -} - -void solo_irq_off(struct solo_dev *solo_dev, u32 mask) -{ - solo_dev->irq_mask &= ~mask; - solo_reg_write(solo_dev, SOLO_IRQ_ENABLE, solo_dev->irq_mask); -} - -/* XXX We should check the return value of the sub-device ISR's */ -static irqreturn_t solo_isr(int irq, void *data) -{ - struct solo_dev *solo_dev = data; - u32 status; - int i; - - status = solo_reg_read(solo_dev, SOLO_IRQ_STAT); - if (!status) - return IRQ_NONE; - - if (status & ~solo_dev->irq_mask) { - solo_reg_write(solo_dev, SOLO_IRQ_STAT, - status & ~solo_dev->irq_mask); - status &= solo_dev->irq_mask; - } - - if (status & SOLO_IRQ_PCI_ERR) { - u32 err = solo_reg_read(solo_dev, SOLO_PCI_ERR); - solo_p2m_error_isr(solo_dev, err); - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_PCI_ERR); - } - - for (i = 0; i < SOLO_NR_P2M; i++) - if (status & SOLO_IRQ_P2M(i)) - solo_p2m_isr(solo_dev, i); - - if (status & SOLO_IRQ_IIC) - solo_i2c_isr(solo_dev); - - if (status & SOLO_IRQ_VIDEO_IN) - solo_video_in_isr(solo_dev); - - /* Call this first so enc gets detected flag set */ - if (status & SOLO_IRQ_MOTION) - solo_motion_isr(solo_dev); - - if (status & SOLO_IRQ_ENCODER) - solo_enc_v4l2_isr(solo_dev); - - if (status & SOLO_IRQ_G723) - solo_g723_isr(solo_dev); - - return IRQ_HANDLED; -} - -static void free_solo_dev(struct solo_dev *solo_dev) -{ - struct pci_dev *pdev; - - if (!solo_dev) - return; - - pdev = solo_dev->pdev; - - /* If we never initialized the PCI device, then nothing else - * below here needs cleanup */ - if (!pdev) { - kfree(solo_dev); - return; - } - - /* Bring down the sub-devices first */ - solo_g723_exit(solo_dev); - solo_enc_v4l2_exit(solo_dev); - solo_enc_exit(solo_dev); - solo_v4l2_exit(solo_dev); - solo_disp_exit(solo_dev); - solo_gpio_exit(solo_dev); - solo_p2m_exit(solo_dev); - solo_i2c_exit(solo_dev); - - /* Now cleanup the PCI device */ - if (solo_dev->reg_base) { - solo_irq_off(solo_dev, ~0); - pci_iounmap(pdev, solo_dev->reg_base); - free_irq(pdev->irq, solo_dev); - } - - pci_release_regions(pdev); - pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); - - kfree(solo_dev); -} - -static int __devinit solo_pci_probe(struct pci_dev *pdev, - const struct pci_device_id *id) -{ - struct solo_dev *solo_dev; - int ret; - int sdram; - u8 chip_id; - u32 reg; - - solo_dev = kzalloc(sizeof(*solo_dev), GFP_KERNEL); - if (solo_dev == NULL) - return -ENOMEM; - - solo_dev->pdev = pdev; - spin_lock_init(&solo_dev->reg_io_lock); - pci_set_drvdata(pdev, solo_dev); - - ret = pci_enable_device(pdev); - if (ret) - goto fail_probe; - - pci_set_master(pdev); - - ret = pci_request_regions(pdev, SOLO6X10_NAME); - if (ret) - goto fail_probe; - - solo_dev->reg_base = pci_ioremap_bar(pdev, 0); - if (solo_dev->reg_base == NULL) { - ret = -ENOMEM; - goto fail_probe; - } - - chip_id = solo_reg_read(solo_dev, SOLO_CHIP_OPTION) & - SOLO_CHIP_ID_MASK; - switch (chip_id) { - case 7: - solo_dev->nr_chans = 16; - solo_dev->nr_ext = 5; - break; - case 6: - solo_dev->nr_chans = 8; - solo_dev->nr_ext = 2; - break; - default: - dev_warn(&pdev->dev, "Invalid chip_id 0x%02x, " - "defaulting to 4 channels\n", - chip_id); - case 5: - solo_dev->nr_chans = 4; - solo_dev->nr_ext = 1; - } - - solo_dev->flags = id->driver_data; - - /* Disable all interrupts to start */ - solo_irq_off(solo_dev, ~0); - - reg = SOLO_SYS_CFG_SDRAM64BIT; - /* Initial global settings */ - if (!(solo_dev->flags & FLAGS_6110)) - reg |= SOLO6010_SYS_CFG_INPUTDIV(25) | - SOLO6010_SYS_CFG_FEEDBACKDIV((SOLO_CLOCK_MHZ * 2) - 2) | - SOLO6010_SYS_CFG_OUTDIV(3); - solo_reg_write(solo_dev, SOLO_SYS_CFG, reg); - - if (solo_dev->flags & FLAGS_6110) { - u32 sys_clock_MHz = SOLO_CLOCK_MHZ; - u32 pll_DIVQ; - u32 pll_DIVF; - - if (sys_clock_MHz < 125) { - pll_DIVQ = 3; - pll_DIVF = (sys_clock_MHz * 4) / 3; - } else { - pll_DIVQ = 2; - pll_DIVF = (sys_clock_MHz * 2) / 3; - } - - solo_reg_write(solo_dev, SOLO6110_PLL_CONFIG, - SOLO6110_PLL_RANGE_5_10MHZ | - SOLO6110_PLL_DIVR(9) | - SOLO6110_PLL_DIVQ_EXP(pll_DIVQ) | - SOLO6110_PLL_DIVF(pll_DIVF) | SOLO6110_PLL_FSEN); - mdelay(1); // PLL Locking time (1ms) - - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 3 << 8); /* ? */ - } else - solo_reg_write(solo_dev, SOLO_DMA_CTRL1, 1 << 8); /* ? */ - - solo_reg_write(solo_dev, SOLO_TIMER_CLOCK_NUM, SOLO_CLOCK_MHZ - 1); - - /* PLL locking time of 1ms */ - mdelay(1); - - ret = request_irq(pdev->irq, solo_isr, IRQF_SHARED, SOLO6X10_NAME, - solo_dev); - if (ret) - goto fail_probe; - - /* Handle this from the start */ - solo_irq_on(solo_dev, SOLO_IRQ_PCI_ERR); - - ret = solo_i2c_init(solo_dev); - if (ret) - goto fail_probe; - - /* Setup the DMA engine */ - sdram = (solo_dev->nr_chans >= 8) ? 2 : 1; - solo_reg_write(solo_dev, SOLO_DMA_CTRL, - SOLO_DMA_CTRL_REFRESH_CYCLE(1) | - SOLO_DMA_CTRL_SDRAM_SIZE(sdram) | - SOLO_DMA_CTRL_SDRAM_CLK_INVERT | - SOLO_DMA_CTRL_READ_CLK_SELECT | - SOLO_DMA_CTRL_LATENCY(1)); - - ret = solo_p2m_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_disp_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_gpio_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_tw28_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_v4l2_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_enc_v4l2_init(solo_dev); - if (ret) - goto fail_probe; - - ret = solo_g723_init(solo_dev); - if (ret) - goto fail_probe; - - return 0; - -fail_probe: - free_solo_dev(solo_dev); - return ret; -} - -static void __devexit solo_pci_remove(struct pci_dev *pdev) -{ - struct solo_dev *solo_dev = pci_get_drvdata(pdev); - - free_solo_dev(solo_dev); -} - -static struct pci_device_id solo_id_table[] = { - /* 6010 based cards */ - {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6010)}, - {PCI_DEVICE(PCI_VENDOR_ID_SOFTLOGIC, PCI_DEVICE_ID_SOLO6110), - .driver_data = FLAGS_6110}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_NEUSOLO_16)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_9)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_SOLO_16)}, - /* 6110 based cards */ - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_4)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_8)}, - {PCI_DEVICE(PCI_VENDOR_ID_BLUECHERRY, PCI_DEVICE_ID_BC_6110_16)}, - {0,} -}; - -MODULE_DEVICE_TABLE(pci, solo_id_table); - -static struct pci_driver solo_pci_driver = { - .name = SOLO6X10_NAME, - .id_table = solo_id_table, - .probe = solo_pci_probe, - .remove = solo_pci_remove, -}; - -static int __init solo_module_init(void) -{ - return pci_register_driver(&solo_pci_driver); -} - -static void __exit solo_module_exit(void) -{ - pci_unregister_driver(&solo_pci_driver); -} - -module_init(solo_module_init); -module_exit(solo_module_exit); diff --git a/drivers/staging/solo6x10/disp.c b/drivers/staging/solo6x10/disp.c deleted file mode 100644 index 884c0eb757c4..000000000000 --- a/drivers/staging/solo6x10/disp.c +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" - -#define SOLO_VCLK_DELAY 3 -#define SOLO_PROGRESSIVE_VSIZE 1024 - -#define SOLO_MOT_THRESH_W 64 -#define SOLO_MOT_THRESH_H 64 -#define SOLO_MOT_THRESH_SIZE 8192 -#define SOLO_MOT_THRESH_REAL (SOLO_MOT_THRESH_W * SOLO_MOT_THRESH_H) -#define SOLO_MOT_FLAG_SIZE 512 -#define SOLO_MOT_FLAG_AREA (SOLO_MOT_FLAG_SIZE * 32) - -static unsigned video_type; -module_param(video_type, uint, 0644); -MODULE_PARM_DESC(video_type, "video_type (0 = NTSC/Default, 1 = PAL)"); - -static void solo_vin_config(struct solo_dev *solo_dev) -{ - solo_dev->vin_hstart = 8; - solo_dev->vin_vstart = 2; - - solo_reg_write(solo_dev, SOLO_SYS_VCLK, - SOLO_VCLK_SELECT(2) | - SOLO_VCLK_VIN1415_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1213_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN1011_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0809_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0607_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0405_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0203_DELAY(SOLO_VCLK_DELAY) | - SOLO_VCLK_VIN0001_DELAY(SOLO_VCLK_DELAY)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_P, - SOLO_VI_H_START(solo_dev->vin_hstart) | - SOLO_VI_V_START(solo_dev->vin_vstart) | - SOLO_VI_V_STOP(solo_dev->vin_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_I_S, - SOLO_VI_H_START(solo_dev->vout_hstart) | - SOLO_VI_V_START(solo_dev->vout_vstart) | - SOLO_VI_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_ACT_P, - SOLO_VI_H_START(0) | - SOLO_VI_V_START(1) | - SOLO_VI_V_STOP(SOLO_PROGRESSIVE_VSIZE)); - - solo_reg_write(solo_dev, SOLO_VI_CH_FORMAT, - SOLO_VI_FD_SEL_MASK(0) | SOLO_VI_PROG_MASK(0)); - - solo_reg_write(solo_dev, SOLO_VI_FMT_CFG, 0); - solo_reg_write(solo_dev, SOLO_VI_PAGE_SW, 2); - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(858) | SOLO_VI_PB_VSIZE(246)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 240)); - } else { - solo_reg_write(solo_dev, SOLO_VI_PB_CONFIG, - SOLO_VI_PB_USER_MODE | SOLO_VI_PB_PAL); - solo_reg_write(solo_dev, SOLO_VI_PB_RANGE_HV, - SOLO_VI_PB_HSIZE(864) | SOLO_VI_PB_VSIZE(294)); - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_V, - SOLO_VI_PB_VSTART(4) | - SOLO_VI_PB_VSTOP(4 + 288)); - } - solo_reg_write(solo_dev, SOLO_VI_PB_ACT_H, SOLO_VI_PB_HSTART(16) | - SOLO_VI_PB_HSTOP(16 + 720)); -} - -static void solo_disp_config(struct solo_dev *solo_dev) -{ - solo_dev->vout_hstart = 6; - solo_dev->vout_vstart = 8; - - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_COLOR, - (0xa0 << 24) | (0x88 << 16) | (0xa0 << 8) | 0x88); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_COLOR, - (0x10 << 24) | (0x8f << 16) | (0x10 << 8) | 0x8f); - solo_reg_write(solo_dev, SOLO_VO_BKG_COLOR, - (16 << 24) | (128 << 16) | (16 << 8) | 128); - - solo_reg_write(solo_dev, SOLO_VO_FMT_ENC, - solo_dev->video_type | - SOLO_VO_USER_COLOR_SET_NAV | - SOLO_VO_NA_COLOR_Y(0) | - SOLO_VO_NA_COLOR_CB(0) | - SOLO_VO_NA_COLOR_CR(0)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_H, - SOLO_VO_H_START(solo_dev->vout_hstart) | - SOLO_VO_H_STOP(solo_dev->vout_hstart + - solo_dev->video_hsize)); - - solo_reg_write(solo_dev, SOLO_VO_ACT_V, - SOLO_VO_V_START(solo_dev->vout_vstart) | - SOLO_VO_V_STOP(solo_dev->vout_vstart + - solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VO_RANGE_HV, - SOLO_VO_H_LEN(solo_dev->video_hsize) | - SOLO_VO_V_LEN(solo_dev->video_vsize)); - - solo_reg_write(solo_dev, SOLO_VI_WIN_SW, 5); - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, SOLO_VO_DISP_ON | - SOLO_VO_DISP_ERASE_COUNT(8) | - SOLO_VO_DISP_BASE(SOLO_DISP_EXT_ADDR)); - - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - - /* Enable channels we support */ - solo_reg_write(solo_dev, SOLO_VI_CH_ENA, (1 << solo_dev->nr_chans) - 1); - - /* Disable the watchdog */ - solo_reg_write(solo_dev, SOLO_WATCHDOG, 0); -} - -static int solo_dma_vin_region(struct solo_dev *solo_dev, u32 off, - u16 val, int reg_size) -{ - u16 buf[64]; - int i; - int ret = 0; - - for (i = 0; i < sizeof(buf) >> 1; i++) - buf[i] = val; - - for (i = 0; i < reg_size; i += sizeof(buf)) - ret |= solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_VIN, 1, buf, - SOLO_MOTION_EXT_ADDR(solo_dev) + off + i, - sizeof(buf)); - - return ret; -} - -void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val) -{ - if (ch > solo_dev->nr_chans) - return; - - solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - (ch * SOLO_MOT_THRESH_SIZE * 2), - val, SOLO_MOT_THRESH_REAL); -} - -/* First 8k is motion flag (512 bytes * 16). Following that is an 8k+8k - * threshold and working table for each channel. Atleast that's what the - * spec says. However, this code (take from rdk) has some mystery 8k - * block right after the flag area, before the first thresh table. */ -static void solo_motion_config(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - /* Clear motion flag area */ - solo_dma_vin_region(solo_dev, i * SOLO_MOT_FLAG_SIZE, 0x0000, - SOLO_MOT_FLAG_SIZE); - - /* Clear working cache table */ - solo_dma_vin_region(solo_dev, SOLO_MOT_FLAG_AREA + - SOLO_MOT_THRESH_SIZE + - (i * SOLO_MOT_THRESH_SIZE * 2), - 0x0000, SOLO_MOT_THRESH_REAL); - - /* Set default threshold table */ - solo_set_motion_threshold(solo_dev, i, SOLO_DEF_MOT_THRESH); - } - - /* Default motion settings */ - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, SOLO_VI_MOTION_EN(0) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_VI_MOT_CTRL, - SOLO_VI_MOTION_FRAME_COUNT(3) | - SOLO_VI_MOTION_SAMPLE_LENGTH(solo_dev->video_hsize / 16) - | /* SOLO_VI_MOTION_INTR_START_STOP | */ - SOLO_VI_MOTION_SAMPLE_COUNT(10)); - - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); -} - -int solo_disp_init(struct solo_dev *solo_dev) -{ - int i; - - solo_dev->video_hsize = 704; - if (video_type == 0) { - solo_dev->video_type = SOLO_VO_FMT_TYPE_NTSC; - solo_dev->video_vsize = 240; - solo_dev->fps = 30; - } else { - solo_dev->video_type = SOLO_VO_FMT_TYPE_PAL; - solo_dev->video_vsize = 288; - solo_dev->fps = 25; - } - - solo_vin_config(solo_dev); - solo_motion_config(solo_dev); - solo_disp_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 1); - - return 0; -} - -void solo_disp_exit(struct solo_dev *solo_dev) -{ - int i; - - solo_irq_off(solo_dev, SOLO_IRQ_MOTION); - - solo_reg_write(solo_dev, SOLO_VO_DISP_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_ZOOM_CTRL, 0); - solo_reg_write(solo_dev, SOLO_VO_FREEZE_CTRL, 0); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(i), 0); - solo_reg_write(solo_dev, SOLO_VI_WIN_ON(i), 0); - } - - /* Set default border */ - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_X(i), 0); - - for (i = 0; i < 5; i++) - solo_reg_write(solo_dev, SOLO_VO_BORDER_Y(i), 0); - - solo_reg_write(solo_dev, SOLO_VO_BORDER_LINE_MASK, 0); - solo_reg_write(solo_dev, SOLO_VO_BORDER_FILL_MASK, 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(0), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(0), 0); - - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_CTRL(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_START(1), 0); - solo_reg_write(solo_dev, SOLO_VO_RECTANGLE_STOP(1), 0); -} diff --git a/drivers/staging/solo6x10/enc.c b/drivers/staging/solo6x10/enc.c deleted file mode 100644 index de502599bb19..000000000000 --- a/drivers/staging/solo6x10/enc.c +++ /dev/null @@ -1,238 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" -#include "osd-font.h" - -#define CAPTURE_MAX_BANDWIDTH 32 /* D1 4channel (D1 == 4) */ -#define OSG_BUFFER_SIZE 1024 - -#define VI_PROG_HSIZE (1280 - 16) -#define VI_PROG_VSIZE (1024 - 16) - -static void solo_capture_config(struct solo_dev *solo_dev) -{ - int i, j; - unsigned long height; - unsigned long width; - unsigned char *buf; - - solo_reg_write(solo_dev, SOLO_CAP_BASE, - SOLO_CAP_MAX_PAGE(SOLO_CAP_EXT_MAX_PAGE * - solo_dev->nr_chans) | - SOLO_CAP_BASE_ADDR(SOLO_CAP_EXT_ADDR(solo_dev) >> 16)); - solo_reg_write(solo_dev, SOLO_CAP_BTW, - (1 << 17) | SOLO_CAP_PROG_BANDWIDTH(2) | - SOLO_CAP_MAX_BANDWIDTH(CAPTURE_MAX_BANDWIDTH)); - - /* Set scale 1, 9 dimension */ - width = solo_dev->video_hsize; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE1, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 2, 10 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize; - solo_reg_write(solo_dev, SOLO_DIM_SCALE2, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 3, 11 dimension */ - width = solo_dev->video_hsize / 2; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE3, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 4, 12 dimension */ - width = solo_dev->video_hsize / 3; - height = solo_dev->video_vsize / 3; - solo_reg_write(solo_dev, SOLO_DIM_SCALE4, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Set scale 5, 13 dimension */ - width = solo_dev->video_hsize / 4; - height = solo_dev->video_vsize / 2; - solo_reg_write(solo_dev, SOLO_DIM_SCALE5, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 8) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Progressive */ - width = VI_PROG_HSIZE; - height = VI_PROG_VSIZE; - solo_reg_write(solo_dev, SOLO_DIM_PROG, - SOLO_DIM_H_MB_NUM(width / 16) | - SOLO_DIM_V_MB_NUM_FRAME(height / 16) | - SOLO_DIM_V_MB_NUM_FIELD(height / 16)); - - /* Clear OSD */ - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, 0); - solo_reg_write(solo_dev, SOLO_VE_OSD_BASE, SOLO_EOSD_EXT_ADDR >> 16); - solo_reg_write(solo_dev, SOLO_VE_OSD_CLR, - 0xF0 << 16 | 0x80 << 8 | 0x80); - solo_reg_write(solo_dev, SOLO_VE_OSD_OPT, 0); - - /* Clear OSG buffer */ - buf = kzalloc(OSG_BUFFER_SIZE, GFP_KERNEL); - if (!buf) - return; - - for (i = 0; i < solo_dev->nr_chans; i++) { - for (j = 0; j < SOLO_EOSD_EXT_SIZE; j += OSG_BUFFER_SIZE) { - solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_MP4E, 1, buf, - SOLO_EOSD_EXT_ADDR + - (i * SOLO_EOSD_EXT_SIZE) + j, - OSG_BUFFER_SIZE); - } - } - kfree(buf); -} - -int solo_osd_print(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - char *str = solo_enc->osd_text; - u8 *buf; - u32 reg = solo_reg_read(solo_dev, SOLO_VE_OSD_CH); - int len = strlen(str); - int i, j; - int x = 1, y = 1; - - if (len == 0) { - reg &= ~(1 << solo_enc->ch); - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - return 0; - } - - buf = kzalloc(SOLO_EOSD_EXT_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; - - for (i = 0; i < len; i++) { - for (j = 0; j < 16; j++) { - buf[(j*2) + (i%2) + ((x + (i/2)) * 32) + (y * 2048)] = - (solo_osd_font[(str[i] * 4) + (j / 4)] - >> ((3 - (j % 4)) * 8)) & 0xff; - } - } - - solo_p2m_dma(solo_dev, 0, 1, buf, SOLO_EOSD_EXT_ADDR + - (solo_enc->ch * SOLO_EOSD_EXT_SIZE), SOLO_EOSD_EXT_SIZE); - reg |= (1 << solo_enc->ch); - solo_reg_write(solo_dev, SOLO_VE_OSD_CH, reg); - - kfree(buf); - - return 0; -} - -static void solo_jpeg_config(struct solo_dev *solo_dev) -{ - u32 reg; - if (solo_dev->flags & FLAGS_6110) - reg = (4 << 24) | (3 << 16) | (2 << 8) | (1 << 0); - else - reg = (2 << 24) | (2 << 16) | (2 << 8) | (2 << 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_TBL, reg); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_L, 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_QP_CH_H, 0); - solo_reg_write(solo_dev, SOLO_VE_JPEG_CFG, - (SOLO_JPEG_EXT_SIZE(solo_dev) & 0xffff0000) | - ((SOLO_JPEG_EXT_ADDR(solo_dev) >> 16) & 0x0000ffff)); - solo_reg_write(solo_dev, SOLO_VE_JPEG_CTRL, 0xffffffff); - /* que limit, samp limit, pos limit */ - solo_reg_write(solo_dev, 0x0688, (0 << 16) | (30 << 8) | 60); -} - -static void solo_mp4e_config(struct solo_dev *solo_dev) -{ - int i; - u32 reg; - - /* We can only use VE_INTR_CTRL(0) if we want to support mjpeg */ - solo_reg_write(solo_dev, SOLO_VE_CFG0, - SOLO_VE_INTR_CTRL(0) | - SOLO_VE_BLOCK_SIZE(SOLO_MP4E_EXT_SIZE(solo_dev) >> 16) | - SOLO_VE_BLOCK_BASE(SOLO_MP4E_EXT_ADDR(solo_dev) >> 16)); - - solo_reg_write(solo_dev, SOLO_VE_CFG1, - SOLO_VE_INSERT_INDEX | SOLO_VE_MOTION_MODE(0)); - - solo_reg_write(solo_dev, SOLO_VE_WMRK_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_VMRK_INIT_KEY, 0); - solo_reg_write(solo_dev, SOLO_VE_WMRK_STRL, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_POLY, 0); - solo_reg_write(solo_dev, SOLO_VE_ENCRYP_INIT, 0); - - reg = SOLO_VE_LITTLE_ENDIAN | SOLO_COMP_ATTR_FCODE(1) | - SOLO_COMP_TIME_INC(0) | SOLO_COMP_TIME_WIDTH(15); - if (solo_dev->flags & FLAGS_6110) - reg |= SOLO_DCT_INTERVAL(10); - else - reg |= SOLO_DCT_INTERVAL(36 / 4); - solo_reg_write(solo_dev, SOLO_VE_ATTR, reg); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_reg_write(solo_dev, SOLO_VE_CH_REF_BASE(i), - (SOLO_EREF_EXT_ADDR(solo_dev) + - (i * SOLO_EREF_EXT_SIZE)) >> 16); - - if (solo_dev->flags & FLAGS_6110) - solo_reg_write(solo_dev, 0x0634, 0x00040008); /* ? */ -} - -int solo_enc_init(struct solo_dev *solo_dev) -{ - int i; - - solo_capture_config(solo_dev); - solo_mp4e_config(solo_dev); - solo_jpeg_config(solo_dev); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } - - solo_irq_on(solo_dev, SOLO_IRQ_ENCODER); - - return 0; -} - -void solo_enc_exit(struct solo_dev *solo_dev) -{ - int i; - - solo_irq_off(solo_dev, SOLO_IRQ_ENCODER); - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(i), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(i), 0); - } -} diff --git a/drivers/staging/solo6x10/g723.c b/drivers/staging/solo6x10/g723.c deleted file mode 100644 index 59274bfca95b..000000000000 --- a/drivers/staging/solo6x10/g723.c +++ /dev/null @@ -1,399 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" -#include "tw28.h" - -#define G723_INTR_ORDER 0 -#define G723_FDMA_PAGES 32 -#define G723_PERIOD_BYTES 48 -#define G723_PERIOD_BLOCK 1024 -#define G723_FRAMES_PER_PAGE 48 - -/* Sets up channels 16-19 for decoding and 0-15 for encoding */ -#define OUTMODE_MASK 0x300 - -#define SAMPLERATE 8000 -#define BITRATE 25 - -/* The solo writes to 1k byte pages, 32 pages, in the dma. Each 1k page - * is broken down to 20 * 48 byte regions (one for each channel possible) - * with the rest of the page being dummy data. */ -#define MAX_BUFFER (G723_PERIOD_BYTES * PERIODS_MAX) -#define IRQ_PAGES 4 /* 0 - 4 */ -#define PERIODS_MIN (1 << IRQ_PAGES) -#define PERIODS_MAX G723_FDMA_PAGES - -struct solo_snd_pcm { - int on; - spinlock_t lock; - struct solo_dev *solo_dev; - unsigned char g723_buf[G723_PERIOD_BYTES]; -}; - -static void solo_g723_config(struct solo_dev *solo_dev) -{ - int clk_div; - - clk_div = SOLO_CLOCK_MHZ / (SAMPLERATE * (BITRATE * 2) * 2); - - solo_reg_write(solo_dev, SOLO_AUDIO_SAMPLE, - SOLO_AUDIO_BITRATE(BITRATE) | - SOLO_AUDIO_CLK_DIV(clk_div)); - - solo_reg_write(solo_dev, SOLO_AUDIO_FDMA_INTR, - SOLO_AUDIO_FDMA_INTERVAL(IRQ_PAGES) | - SOLO_AUDIO_INTR_ORDER(G723_INTR_ORDER) | - SOLO_AUDIO_FDMA_BASE(SOLO_G723_EXT_ADDR(solo_dev) >> 16)); - - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, - SOLO_AUDIO_ENABLE | SOLO_AUDIO_I2S_MODE | - SOLO_AUDIO_I2S_MULTI(3) | SOLO_AUDIO_MODE(OUTMODE_MASK)); -} - -void solo_g723_isr(struct solo_dev *solo_dev) -{ - struct snd_pcm_str *pstr = - &solo_dev->snd_pcm->streams[SNDRV_PCM_STREAM_CAPTURE]; - struct snd_pcm_substream *ss; - struct solo_snd_pcm *solo_pcm; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_G723); - - for (ss = pstr->substream; ss != NULL; ss = ss->next) { - if (snd_pcm_substream_chip(ss) == NULL) - continue; - - /* This means open() hasn't been called on this one */ - if (snd_pcm_substream_chip(ss) == solo_dev) - continue; - - /* Haven't triggered a start yet */ - solo_pcm = snd_pcm_substream_chip(ss); - if (!solo_pcm->on) - continue; - - snd_pcm_period_elapsed(ss); - } -} - -static int snd_solo_hw_params(struct snd_pcm_substream *ss, - struct snd_pcm_hw_params *hw_params) -{ - return snd_pcm_lib_malloc_pages(ss, params_buffer_bytes(hw_params)); -} - -static int snd_solo_hw_free(struct snd_pcm_substream *ss) -{ - return snd_pcm_lib_free_pages(ss); -} - -static struct snd_pcm_hardware snd_solo_pcm_hw = { - .info = (SNDRV_PCM_INFO_MMAP | - SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER | - SNDRV_PCM_INFO_MMAP_VALID), - .formats = SNDRV_PCM_FMTBIT_U8, - .rates = SNDRV_PCM_RATE_8000, - .rate_min = 8000, - .rate_max = 8000, - .channels_min = 1, - .channels_max = 1, - .buffer_bytes_max = MAX_BUFFER, - .period_bytes_min = G723_PERIOD_BYTES, - .period_bytes_max = G723_PERIOD_BYTES, - .periods_min = PERIODS_MIN, - .periods_max = PERIODS_MAX, -}; - -static int snd_solo_pcm_open(struct snd_pcm_substream *ss) -{ - struct solo_dev *solo_dev = snd_pcm_substream_chip(ss); - struct solo_snd_pcm *solo_pcm; - - solo_pcm = kzalloc(sizeof(*solo_pcm), GFP_KERNEL); - if (solo_pcm == NULL) - return -ENOMEM; - - spin_lock_init(&solo_pcm->lock); - solo_pcm->solo_dev = solo_dev; - ss->runtime->hw = snd_solo_pcm_hw; - - snd_pcm_substream_chip(ss) = solo_pcm; - - return 0; -} - -static int snd_solo_pcm_close(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - - snd_pcm_substream_chip(ss) = solo_pcm->solo_dev; - kfree(solo_pcm); - - return 0; -} - -static int snd_solo_pcm_trigger(struct snd_pcm_substream *ss, int cmd) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - int ret = 0; - - spin_lock(&solo_pcm->lock); - - switch (cmd) { - case SNDRV_PCM_TRIGGER_START: - if (solo_pcm->on == 0) { - /* If this is the first user, switch on interrupts */ - if (atomic_inc_return(&solo_dev->snd_users) == 1) - solo_irq_on(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 1; - } - break; - case SNDRV_PCM_TRIGGER_STOP: - if (solo_pcm->on) { - /* If this was our last user, switch them off */ - if (atomic_dec_return(&solo_dev->snd_users) == 0) - solo_irq_off(solo_dev, SOLO_IRQ_G723); - solo_pcm->on = 0; - } - break; - default: - ret = -EINVAL; - } - - spin_unlock(&solo_pcm->lock); - - return ret; -} - -static int snd_solo_pcm_prepare(struct snd_pcm_substream *ss) -{ - return 0; -} - -static snd_pcm_uframes_t snd_solo_pcm_pointer(struct snd_pcm_substream *ss) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - snd_pcm_uframes_t idx = solo_reg_read(solo_dev, SOLO_AUDIO_STA) & 0x1f; - - return idx * G723_FRAMES_PER_PAGE; -} - -static int snd_solo_pcm_copy(struct snd_pcm_substream *ss, int channel, - snd_pcm_uframes_t pos, void __user *dst, - snd_pcm_uframes_t count) -{ - struct solo_snd_pcm *solo_pcm = snd_pcm_substream_chip(ss); - struct solo_dev *solo_dev = solo_pcm->solo_dev; - int err, i; - - for (i = 0; i < (count / G723_FRAMES_PER_PAGE); i++) { - int page = (pos / G723_FRAMES_PER_PAGE) + i; - - err = solo_p2m_dma(solo_dev, SOLO_P2M_DMA_ID_G723E, 0, - solo_pcm->g723_buf, - SOLO_G723_EXT_ADDR(solo_dev) + - (page * G723_PERIOD_BLOCK) + - (ss->number * G723_PERIOD_BYTES), - G723_PERIOD_BYTES); - if (err) - return err; - - err = copy_to_user(dst + (i * G723_PERIOD_BYTES), - solo_pcm->g723_buf, G723_PERIOD_BYTES); - - if (err) - return -EFAULT; - } - - return 0; -} - -static struct snd_pcm_ops snd_solo_pcm_ops = { - .open = snd_solo_pcm_open, - .close = snd_solo_pcm_close, - .ioctl = snd_pcm_lib_ioctl, - .hw_params = snd_solo_hw_params, - .hw_free = snd_solo_hw_free, - .prepare = snd_solo_pcm_prepare, - .trigger = snd_solo_pcm_trigger, - .pointer = snd_solo_pcm_pointer, - .copy = snd_solo_pcm_copy, -}; - -static int snd_solo_capture_volume_info(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_info *info) -{ - info->type = SNDRV_CTL_ELEM_TYPE_INTEGER; - info->count = 1; - info->value.integer.min = 0; - info->value.integer.max = 15; - info->value.integer.step = 1; - - return 0; -} - -static int snd_solo_capture_volume_get(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - - value->value.integer.value[0] = tw28_get_audio_gain(solo_dev, ch); - - return 0; -} - -static int snd_solo_capture_volume_put(struct snd_kcontrol *kcontrol, - struct snd_ctl_elem_value *value) -{ - struct solo_dev *solo_dev = snd_kcontrol_chip(kcontrol); - u8 ch = value->id.numid - 1; - u8 old_val; - - old_val = tw28_get_audio_gain(solo_dev, ch); - if (old_val == value->value.integer.value[0]) - return 0; - - tw28_set_audio_gain(solo_dev, ch, value->value.integer.value[0]); - - return 1; -} - -static struct snd_kcontrol_new snd_solo_capture_volume = { - .iface = SNDRV_CTL_ELEM_IFACE_MIXER, - .name = "Capture Volume", - .info = snd_solo_capture_volume_info, - .get = snd_solo_capture_volume_get, - .put = snd_solo_capture_volume_put, -}; - -static int solo_snd_pcm_init(struct solo_dev *solo_dev) -{ - struct snd_card *card = solo_dev->snd_card; - struct snd_pcm *pcm; - struct snd_pcm_substream *ss; - int ret; - int i; - - ret = snd_pcm_new(card, card->driver, 0, 0, solo_dev->nr_chans, - &pcm); - if (ret < 0) - return ret; - - snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, - &snd_solo_pcm_ops); - - snd_pcm_chip(pcm) = solo_dev; - pcm->info_flags = 0; - strcpy(pcm->name, card->shortname); - - for (i = 0, ss = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; - ss; ss = ss->next, i++) - sprintf(ss->name, "Camera #%d Audio", i); - - ret = snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data(GFP_KERNEL), - MAX_BUFFER, MAX_BUFFER); - if (ret < 0) - return ret; - - solo_dev->snd_pcm = pcm; - - return 0; -} - -int solo_g723_init(struct solo_dev *solo_dev) -{ - static struct snd_device_ops ops = { NULL }; - struct snd_card *card; - struct snd_kcontrol_new kctl; - char name[32]; - int ret; - - atomic_set(&solo_dev->snd_users, 0); - - /* Allows for easier mapping between video and audio */ - sprintf(name, "Softlogic%d", solo_dev->vfd->num); - - ret = snd_card_create(SNDRV_DEFAULT_IDX1, name, THIS_MODULE, 0, - &solo_dev->snd_card); - if (ret < 0) - return ret; - - card = solo_dev->snd_card; - - strcpy(card->driver, SOLO6X10_NAME); - strcpy(card->shortname, "SOLO-6x10 Audio"); - sprintf(card->longname, "%s on %s IRQ %d", card->shortname, - pci_name(solo_dev->pdev), solo_dev->pdev->irq); - snd_card_set_dev(card, &solo_dev->pdev->dev); - - ret = snd_device_new(card, SNDRV_DEV_LOWLEVEL, solo_dev, &ops); - if (ret < 0) - goto snd_error; - - /* Mixer controls */ - strcpy(card->mixername, "SOLO-6x10"); - kctl = snd_solo_capture_volume; - kctl.count = solo_dev->nr_chans; - ret = snd_ctl_add(card, snd_ctl_new1(&kctl, solo_dev)); - if (ret < 0) - return ret; - - ret = solo_snd_pcm_init(solo_dev); - if (ret < 0) - goto snd_error; - - ret = snd_card_register(card); - if (ret < 0) - goto snd_error; - - solo_g723_config(solo_dev); - - dev_info(&solo_dev->pdev->dev, "Alsa sound card as %s\n", name); - - return 0; - -snd_error: - snd_card_free(card); - return ret; -} - -void solo_g723_exit(struct solo_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_AUDIO_CONTROL, 0); - solo_irq_off(solo_dev, SOLO_IRQ_G723); - - snd_card_free(solo_dev->snd_card); -} diff --git a/drivers/staging/solo6x10/gpio.c b/drivers/staging/solo6x10/gpio.c deleted file mode 100644 index 0925e6f33a99..000000000000 --- a/drivers/staging/solo6x10/gpio.c +++ /dev/null @@ -1,102 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" - -static void solo_gpio_mode(struct solo_dev *solo_dev, - unsigned int port_mask, unsigned int mode) -{ - int port; - unsigned int ret; - - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_0); - - /* To set gpio */ - for (port = 0; port < 16; port++) { - if (!((1 << port) & port_mask)) - continue; - - ret &= (~(3 << (port << 1))); - ret |= ((mode & 3) << (port << 1)); - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_0, ret); - - /* To set extended gpio - sensor */ - ret = solo_reg_read(solo_dev, SOLO_GPIO_CONFIG_1); - - for (port = 0; port < 16; port++) { - if (!((1 << (port + 16)) & port_mask)) - continue; - - if (!mode) - ret &= ~(1 << port); - else - ret |= 1 << port; - } - - solo_reg_write(solo_dev, SOLO_GPIO_CONFIG_1, ret); -} - -static void solo_gpio_set(struct solo_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) | value); -} - -static void solo_gpio_clear(struct solo_dev *solo_dev, unsigned int value) -{ - solo_reg_write(solo_dev, SOLO_GPIO_DATA_OUT, - solo_reg_read(solo_dev, SOLO_GPIO_DATA_OUT) & ~value); -} - -static void solo_gpio_config(struct solo_dev *solo_dev) -{ - /* Video reset */ - solo_gpio_mode(solo_dev, 0x30, 1); - solo_gpio_clear(solo_dev, 0x30); - udelay(100); - solo_gpio_set(solo_dev, 0x30); - udelay(100); - - /* Warning: Don't touch the next line unless you're sure of what - * you're doing: first four gpio [0-3] are used for video. */ - solo_gpio_mode(solo_dev, 0x0f, 2); - - /* We use bit 8-15 of SOLO_GPIO_CONFIG_0 for relay purposes */ - solo_gpio_mode(solo_dev, 0xff00, 1); - - /* Initially set relay status to 0 */ - solo_gpio_clear(solo_dev, 0xff00); -} - -int solo_gpio_init(struct solo_dev *solo_dev) -{ - solo_gpio_config(solo_dev); - return 0; -} - -void solo_gpio_exit(struct solo_dev *solo_dev) -{ - solo_gpio_clear(solo_dev, 0x30); - solo_gpio_config(solo_dev); -} diff --git a/drivers/staging/solo6x10/i2c.c b/drivers/staging/solo6x10/i2c.c deleted file mode 100644 index ef95a500b4da..000000000000 --- a/drivers/staging/solo6x10/i2c.c +++ /dev/null @@ -1,330 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - */ - -/* XXX: The SOLO6x10 i2c does not have separate interrupts for each i2c - * channel. The bus can only handle one i2c event at a time. The below handles - * this all wrong. We should be using the status registers to see if the bus - * is in use, and have a global lock to check the status register. Also, - * the bulk of the work should be handled out-of-interrupt. The ugly loops - * that occur during interrupt scare me. The ISR should merely signal - * thread context, ACK the interrupt, and move on. -- BenC */ - -#include -#include "solo6x10.h" - -u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off) -{ - struct i2c_msg msgs[2]; - u8 data; - - msgs[0].flags = 0; - msgs[0].addr = addr; - msgs[0].len = 1; - msgs[0].buf = &off; - - msgs[1].flags = I2C_M_RD; - msgs[1].addr = addr; - msgs[1].len = 1; - msgs[1].buf = &data; - - i2c_transfer(&solo_dev->i2c_adap[id], msgs, 2); - - return data; -} - -void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, - u8 off, u8 data) -{ - struct i2c_msg msgs; - u8 buf[2]; - - buf[0] = off; - buf[1] = data; - msgs.flags = 0; - msgs.addr = addr; - msgs.len = 2; - msgs.buf = buf; - - i2c_transfer(&solo_dev->i2c_adap[id], &msgs, 1); -} - -static void solo_i2c_flush(struct solo_dev *solo_dev, int wr) -{ - u32 ctrl; - - ctrl = SOLO_IIC_CH_SET(solo_dev->i2c_id); - - if (solo_dev->i2c_state == IIC_STATE_START) - ctrl |= SOLO_IIC_START; - - if (wr) { - ctrl |= SOLO_IIC_WRITE; - } else { - ctrl |= SOLO_IIC_READ; - if (!(solo_dev->i2c_msg->flags & I2C_M_NO_RD_ACK)) - ctrl |= SOLO_IIC_ACK_EN; - } - - if (solo_dev->i2c_msg_ptr == solo_dev->i2c_msg->len) - ctrl |= SOLO_IIC_STOP; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, ctrl); -} - -static void solo_i2c_start(struct solo_dev *solo_dev) -{ - u32 addr = solo_dev->i2c_msg->addr << 1; - - if (solo_dev->i2c_msg->flags & I2C_M_RD) - addr |= 1; - - solo_dev->i2c_state = IIC_STATE_START; - solo_reg_write(solo_dev, SOLO_IIC_TXD, addr); - solo_i2c_flush(solo_dev, 1); -} - -static void solo_i2c_stop(struct solo_dev *solo_dev) -{ - solo_irq_off(solo_dev, SOLO_IRQ_IIC); - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo_dev->i2c_state = IIC_STATE_STOP; - wake_up(&solo_dev->i2c_wait); -} - -static int solo_i2c_handle_read(struct solo_dev *solo_dev) -{ -prepare_read: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_i2c_flush(solo_dev, 0); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - goto prepare_read; - else - solo_i2c_stop(solo_dev); - } - - return 0; -} - -static int solo_i2c_handle_write(struct solo_dev *solo_dev) -{ -retry_write: - if (solo_dev->i2c_msg_ptr != solo_dev->i2c_msg->len) { - solo_reg_write(solo_dev, SOLO_IIC_TXD, - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr]); - solo_dev->i2c_msg_ptr++; - solo_i2c_flush(solo_dev, 1); - return 0; - } - - solo_dev->i2c_msg_ptr = 0; - solo_dev->i2c_msg++; - solo_dev->i2c_msg_num--; - - if (solo_dev->i2c_msg_num == 0) { - solo_i2c_stop(solo_dev); - return 0; - } - - if (!(solo_dev->i2c_msg->flags & I2C_M_NOSTART)) { - solo_i2c_start(solo_dev); - } else { - if (solo_dev->i2c_msg->flags & I2C_M_RD) - solo_i2c_stop(solo_dev); - else - goto retry_write; - } - - return 0; -} - -int solo_i2c_isr(struct solo_dev *solo_dev) -{ - u32 status = solo_reg_read(solo_dev, SOLO_IIC_CTRL); - int ret = -EINVAL; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_IIC); - - if (status & (SOLO_IIC_STATE_TRNS & SOLO_IIC_STATE_SIG_ERR) || - solo_dev->i2c_id < 0) { - solo_i2c_stop(solo_dev); - return -ENXIO; - } - - switch (solo_dev->i2c_state) { - case IIC_STATE_START: - if (solo_dev->i2c_msg->flags & I2C_M_RD) { - solo_dev->i2c_state = IIC_STATE_READ; - ret = solo_i2c_handle_read(solo_dev); - break; - } - - solo_dev->i2c_state = IIC_STATE_WRITE; - case IIC_STATE_WRITE: - ret = solo_i2c_handle_write(solo_dev); - break; - - case IIC_STATE_READ: - solo_dev->i2c_msg->buf[solo_dev->i2c_msg_ptr] = - solo_reg_read(solo_dev, SOLO_IIC_RXD); - solo_dev->i2c_msg_ptr++; - - ret = solo_i2c_handle_read(solo_dev); - break; - - default: - solo_i2c_stop(solo_dev); - } - - return ret; -} - -static int solo_i2c_master_xfer(struct i2c_adapter *adap, - struct i2c_msg msgs[], int num) -{ - struct solo_dev *solo_dev = adap->algo_data; - unsigned long timeout; - int ret; - int i; - DEFINE_WAIT(wait); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (&solo_dev->i2c_adap[i] == adap) - break; - } - - if (i == SOLO_I2C_ADAPTERS) - return num; /* XXX Right return value for failure? */ - - mutex_lock(&solo_dev->i2c_mutex); - solo_dev->i2c_id = i; - solo_dev->i2c_msg = msgs; - solo_dev->i2c_msg_num = num; - solo_dev->i2c_msg_ptr = 0; - - solo_reg_write(solo_dev, SOLO_IIC_CTRL, 0); - solo_irq_on(solo_dev, SOLO_IRQ_IIC); - solo_i2c_start(solo_dev); - - timeout = HZ / 2; - - for (;;) { - prepare_to_wait(&solo_dev->i2c_wait, &wait, TASK_INTERRUPTIBLE); - - if (solo_dev->i2c_state == IIC_STATE_STOP) - break; - - timeout = schedule_timeout(timeout); - if (!timeout) - break; - - if (signal_pending(current)) - break; - } - - finish_wait(&solo_dev->i2c_wait, &wait); - ret = num - solo_dev->i2c_msg_num; - solo_dev->i2c_state = IIC_STATE_IDLE; - solo_dev->i2c_id = -1; - - mutex_unlock(&solo_dev->i2c_mutex); - - return ret; -} - -static u32 solo_i2c_functionality(struct i2c_adapter *adap) -{ - return I2C_FUNC_I2C; -} - -static struct i2c_algorithm solo_i2c_algo = { - .master_xfer = solo_i2c_master_xfer, - .functionality = solo_i2c_functionality, -}; - -int solo_i2c_init(struct solo_dev *solo_dev) -{ - int i; - int ret; - - solo_reg_write(solo_dev, SOLO_IIC_CFG, - SOLO_IIC_PRESCALE(8) | SOLO_IIC_ENABLE); - - solo_dev->i2c_id = -1; - solo_dev->i2c_state = IIC_STATE_IDLE; - init_waitqueue_head(&solo_dev->i2c_wait); - mutex_init(&solo_dev->i2c_mutex); - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - struct i2c_adapter *adap = &solo_dev->i2c_adap[i]; - - snprintf(adap->name, I2C_NAME_SIZE, "%s I2C %d", SOLO6X10_NAME, i); - adap->algo = &solo_i2c_algo; - adap->algo_data = solo_dev; - adap->retries = 1; - adap->dev.parent = &solo_dev->pdev->dev; - - ret = i2c_add_adapter(adap); - if (ret) { - adap->algo_data = NULL; - break; - } - } - - if (ret) { - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - break; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } - return ret; - } - - dev_info(&solo_dev->pdev->dev, "Enabled %d i2c adapters\n", - SOLO_I2C_ADAPTERS); - - return 0; -} - -void solo_i2c_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_I2C_ADAPTERS; i++) { - if (!solo_dev->i2c_adap[i].algo_data) - continue; - i2c_del_adapter(&solo_dev->i2c_adap[i]); - solo_dev->i2c_adap[i].algo_data = NULL; - } -} diff --git a/drivers/staging/solo6x10/jpeg.h b/drivers/staging/solo6x10/jpeg.h deleted file mode 100644 index 50defec318cc..000000000000 --- a/drivers/staging/solo6x10/jpeg.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 __SOLO6X10_JPEG_H -#define __SOLO6X10_JPEG_H - -static unsigned char jpeg_header[] = { - 0xff, 0xd8, 0xff, 0xfe, 0x00, 0x0d, 0x42, 0x6c, - 0x75, 0x65, 0x63, 0x68, 0x65, 0x72, 0x72, 0x79, - 0x20, 0xff, 0xdb, 0x00, 0x43, 0x00, 0x20, 0x16, - 0x18, 0x1c, 0x18, 0x14, 0x20, 0x1c, 0x1a, 0x1c, - 0x24, 0x22, 0x20, 0x26, 0x30, 0x50, 0x34, 0x30, - 0x2c, 0x2c, 0x30, 0x62, 0x46, 0x4a, 0x3a, 0x50, - 0x74, 0x66, 0x7a, 0x78, 0x72, 0x66, 0x70, 0x6e, - 0x80, 0x90, 0xb8, 0x9c, 0x80, 0x88, 0xae, 0x8a, - 0x6e, 0x70, 0xa0, 0xda, 0xa2, 0xae, 0xbe, 0xc4, - 0xce, 0xd0, 0xce, 0x7c, 0x9a, 0xe2, 0xf2, 0xe0, - 0xc8, 0xf0, 0xb8, 0xca, 0xce, 0xc6, 0xff, 0xdb, - 0x00, 0x43, 0x01, 0x22, 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, 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, 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, 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, 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, 0x02, 0xc0, - 0x03, 0x01, 0x22, 0x00, 0x02, 0x11, 0x01, 0x03, - 0x11, 0x01, 0xff, 0xda, 0x00, 0x0c, 0x03, 0x01, - 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00 -}; - -/* This is the byte marker for the start of SOF0: 0xffc0 marker */ -#define SOF0_START 575 - -#endif /* __SOLO6X10_JPEG_H */ diff --git a/drivers/staging/solo6x10/offsets.h b/drivers/staging/solo6x10/offsets.h deleted file mode 100644 index 3d7e569f1cf8..000000000000 --- a/drivers/staging/solo6x10/offsets.h +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 __SOLO6X10_OFFSETS_H -#define __SOLO6X10_OFFSETS_H - -/* Offsets and sizes of the external address */ -#define SOLO_DISP_EXT_ADDR 0x00000000 -#define SOLO_DISP_EXT_SIZE 0x00480000 - -#define SOLO_DEC2LIVE_EXT_ADDR (SOLO_DISP_EXT_ADDR + SOLO_DISP_EXT_SIZE) -#define SOLO_DEC2LIVE_EXT_SIZE 0x00240000 - -#define SOLO_OSG_EXT_ADDR (SOLO_DEC2LIVE_EXT_ADDR + SOLO_DEC2LIVE_EXT_SIZE) -#define SOLO_OSG_EXT_SIZE 0x00120000 - -#define SOLO_EOSD_EXT_ADDR (SOLO_OSG_EXT_ADDR + SOLO_OSG_EXT_SIZE) -#define SOLO_EOSD_EXT_SIZE 0x00010000 - -#define SOLO_MOTION_EXT_ADDR(__solo) (SOLO_EOSD_EXT_ADDR + \ - (SOLO_EOSD_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MOTION_EXT_SIZE 0x00080000 - -#define SOLO_G723_EXT_ADDR(__solo) \ - (SOLO_MOTION_EXT_ADDR(__solo) + SOLO_MOTION_EXT_SIZE) -#define SOLO_G723_EXT_SIZE 0x00010000 - -#define SOLO_CAP_EXT_ADDR(__solo) \ - (SOLO_G723_EXT_ADDR(__solo) + SOLO_G723_EXT_SIZE) -#define SOLO_CAP_EXT_MAX_PAGE (18 + 15) -#define SOLO_CAP_EXT_SIZE (SOLO_CAP_EXT_MAX_PAGE * 65536) - -/* This +1 is very important -- Why?! -- BenC */ -#define SOLO_EREF_EXT_ADDR(__solo) \ - (SOLO_CAP_EXT_ADDR(__solo) + \ - (SOLO_CAP_EXT_SIZE * (__solo->nr_chans + 1))) -#define SOLO_EREF_EXT_SIZE 0x00140000 - -#define SOLO_MP4E_EXT_ADDR(__solo) \ - (SOLO_EREF_EXT_ADDR(__solo) + \ - (SOLO_EREF_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MP4E_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) - -#define SOLO_DREF_EXT_ADDR(__solo) \ - (SOLO_MP4E_EXT_ADDR(__solo) + SOLO_MP4E_EXT_SIZE(__solo)) -#define SOLO_DREF_EXT_SIZE 0x00140000 - -#define SOLO_MP4D_EXT_ADDR(__solo) \ - (SOLO_DREF_EXT_ADDR(__solo) + \ - (SOLO_DREF_EXT_SIZE * __solo->nr_chans)) -#define SOLO_MP4D_EXT_SIZE 0x00080000 - -#define SOLO_JPEG_EXT_ADDR(__solo) \ - (SOLO_MP4D_EXT_ADDR(__solo) + \ - (SOLO_MP4D_EXT_SIZE * __solo->nr_chans)) -#define SOLO_JPEG_EXT_SIZE(__solo) (0x00080000 * __solo->nr_chans) - -#endif /* __SOLO6X10_OFFSETS_H */ diff --git a/drivers/staging/solo6x10/osd-font.h b/drivers/staging/solo6x10/osd-font.h deleted file mode 100644 index 591e0e82e0e8..000000000000 --- a/drivers/staging/solo6x10/osd-font.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 __SOLO6X10_OSD_FONT_H -#define __SOLO6X10_OSD_FONT_H - -static const unsigned int solo_osd_font[] = { - 0x00000000, 0x0000c0c8, 0xccfefe0c, 0x08000000, - 0x00000000, 0x10103838, 0x7c7cfefe, 0x00000000, /* 0 */ - 0x00000000, 0xfefe7c7c, 0x38381010, 0x10000000, - 0x00000000, 0x7c82fefe, 0xfefefe7c, 0x00000000, - 0x00000000, 0x00001038, 0x10000000, 0x00000000, - 0x00000000, 0x0010387c, 0xfe7c3810, 0x00000000, - 0x00000000, 0x00384444, 0x44380000, 0x00000000, - 0x00000000, 0x38448282, 0x82443800, 0x00000000, - 0x00000000, 0x007c7c7c, 0x7c7c0000, 0x00000000, - 0x00000000, 0x6c6c6c6c, 0x6c6c6c6c, 0x00000000, - 0x00000000, 0x061e7efe, 0xfe7e1e06, 0x00000000, - 0x00000000, 0xc0f0fcfe, 0xfefcf0c0, 0x00000000, - 0x00000000, 0xc6cedefe, 0xfedecec6, 0x00000000, - 0x00000000, 0xc6e6f6fe, 0xfef6e6c6, 0x00000000, - 0x00000000, 0x12367efe, 0xfe7e3612, 0x00000000, - 0x00000000, 0x90d8fcfe, 0xfefcd890, 0x00000000, - 0x00000038, 0x7cc692ba, 0x92c67c38, 0x00000000, - 0x00000038, 0x7cc6aa92, 0xaac67c38, 0x00000000, - 0x00000038, 0x7830107c, 0xbaa8680c, 0x00000000, - 0x00000038, 0x3c18127c, 0xb8382c60, 0x00000000, - 0x00000044, 0xaa6c8254, 0x38eec67c, 0x00000000, - 0x00000082, 0x44288244, 0x38c6827c, 0x00000000, - 0x00000038, 0x444444fe, 0xfeeec6fe, 0x00000000, - 0x00000018, 0x78187818, 0x3c7e7e3c, 0x00000000, - 0x00000000, 0x3854929a, 0x82443800, 0x00000000, - 0x00000000, 0x00c0c8cc, 0xfefe0c08, 0x00000000, - 0x0000e0a0, 0xe040e00e, 0x8a0ea40e, 0x00000000, - 0x0000e0a0, 0xe040e00e, 0x0a8e440e, 0x00000000, - 0x0000007c, 0x82829292, 0x929282fe, 0x00000000, - 0x000000f8, 0xfc046494, 0x946404fc, 0x00000000, - 0x0000003f, 0x7f404c52, 0x524c407f, 0x00000000, - 0x0000007c, 0x82ba82ba, 0x82ba82fe, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00000000, - 0x00000000, 0x183c3c3c, 0x18180018, 0x18000000, /* 32 ! */ - 0x00000066, 0x66240000, 0x00000000, 0x00000000, - 0x00000000, 0x6c6cfe6c, 0x6c6cfe6c, 0x6c000000, /* 34 " # */ - 0x00001010, 0x7cd6d616, 0x7cd0d6d6, 0x7c101000, - 0x00000000, 0x0086c660, 0x30180cc6, 0xc2000000, /* 36 $ % */ - 0x00000000, 0x386c6c38, 0xdc766666, 0xdc000000, - 0x0000000c, 0x0c0c0600, 0x00000000, 0x00000000, /* 38 & ' */ - 0x00000000, 0x30180c0c, 0x0c0c0c18, 0x30000000, - 0x00000000, 0x0c183030, 0x30303018, 0x0c000000, /* 40 ( ) */ - 0x00000000, 0x0000663c, 0xff3c6600, 0x00000000, - 0x00000000, 0x00001818, 0x7e181800, 0x00000000, /* 42 * + */ - 0x00000000, 0x00000000, 0x00000e0e, 0x0c060000, - 0x00000000, 0x00000000, 0x7e000000, 0x00000000, /* 44 , - */ - 0x00000000, 0x00000000, 0x00000006, 0x06000000, - 0x00000000, 0x80c06030, 0x180c0602, 0x00000000, /* 46 . / */ - 0x0000007c, 0xc6e6f6de, 0xcec6c67c, 0x00000000, - 0x00000030, 0x383c3030, 0x303030fc, 0x00000000, /* 48 0 1 */ - 0x0000007c, 0xc6c06030, 0x180cc6fe, 0x00000000, - 0x0000007c, 0xc6c0c07c, 0xc0c0c67c, 0x00000000, /* 50 2 3 */ - 0x00000060, 0x70786c66, 0xfe6060f0, 0x00000000, - 0x000000fe, 0x0606067e, 0xc0c0c67c, 0x00000000, /* 52 4 5 */ - 0x00000038, 0x0c06067e, 0xc6c6c67c, 0x00000000, - 0x000000fe, 0xc6c06030, 0x18181818, 0x00000000, /* 54 6 7 */ - 0x0000007c, 0xc6c6c67c, 0xc6c6c67c, 0x00000000, - 0x0000007c, 0xc6c6c6fc, 0xc0c06038, 0x00000000, /* 56 8 9 */ - 0x00000000, 0x18180000, 0x00181800, 0x00000000, - 0x00000000, 0x18180000, 0x0018180c, 0x00000000, /* 58 : ; */ - 0x00000060, 0x30180c06, 0x0c183060, 0x00000000, - 0x00000000, 0x007e0000, 0x007e0000, 0x00000000, - 0x00000006, 0x0c183060, 0x30180c06, 0x00000000, - 0x0000007c, 0xc6c66030, 0x30003030, 0x00000000, - 0x0000007c, 0xc6f6d6d6, 0x7606067c, 0x00000000, - 0x00000010, 0x386cc6c6, 0xfec6c6c6, 0x00000000, /* 64 @ A */ - 0x0000007e, 0xc6c6c67e, 0xc6c6c67e, 0x00000000, - 0x00000078, 0xcc060606, 0x0606cc78, 0x00000000, /* 66 */ - 0x0000003e, 0x66c6c6c6, 0xc6c6663e, 0x00000000, - 0x000000fe, 0x0606063e, 0x060606fe, 0x00000000, /* 68 */ - 0x000000fe, 0x0606063e, 0x06060606, 0x00000000, - 0x00000078, 0xcc060606, 0xf6c6ccb8, 0x00000000, /* 70 */ - 0x000000c6, 0xc6c6c6fe, 0xc6c6c6c6, 0x00000000, - 0x0000003c, 0x18181818, 0x1818183c, 0x00000000, /* 72 */ - 0x00000060, 0x60606060, 0x6066663c, 0x00000000, - 0x000000c6, 0xc666361e, 0x3666c6c6, 0x00000000, /* 74 */ - 0x00000006, 0x06060606, 0x060606fe, 0x00000000, - 0x000000c6, 0xeefed6c6, 0xc6c6c6c6, 0x00000000, /* 76 */ - 0x000000c6, 0xcedefef6, 0xe6c6c6c6, 0x00000000, - 0x00000038, 0x6cc6c6c6, 0xc6c66c38, 0x00000000, /* 78 */ - 0x0000007e, 0xc6c6c67e, 0x06060606, 0x00000000, - 0x00000038, 0x6cc6c6c6, 0xc6d67c38, 0x60000000, /* 80 */ - 0x0000007e, 0xc6c6c67e, 0x66c6c6c6, 0x00000000, - 0x0000007c, 0xc6c60c38, 0x60c6c67c, 0x00000000, /* 82 */ - 0x0000007e, 0x18181818, 0x18181818, 0x00000000, - 0x000000c6, 0xc6c6c6c6, 0xc6c6c67c, 0x00000000, /* 84 */ - 0x000000c6, 0xc6c6c6c6, 0xc66c3810, 0x00000000, - 0x000000c6, 0xc6c6c6c6, 0xd6d6fe6c, 0x00000000, /* 86 */ - 0x000000c6, 0xc6c66c38, 0x6cc6c6c6, 0x00000000, - 0x00000066, 0x66666666, 0x3c181818, 0x00000000, /* 88 */ - 0x000000fe, 0xc0603018, 0x0c0606fe, 0x00000000, - 0x0000003c, 0x0c0c0c0c, 0x0c0c0c3c, 0x00000000, /* 90 */ - 0x00000002, 0x060c1830, 0x60c08000, 0x00000000, - 0x0000003c, 0x30303030, 0x3030303c, 0x00000000, /* 92 */ - 0x00001038, 0x6cc60000, 0x00000000, 0x00000000, - 0x00000000, 0x00000000, 0x00000000, 0x00fe0000, - 0x00001818, 0x30000000, 0x00000000, 0x00000000, - 0x00000000, 0x00003c60, 0x7c66667c, 0x00000000, - 0x0000000c, 0x0c0c7ccc, 0xcccccc7c, 0x00000000, - 0x00000000, 0x00007cc6, 0x0606c67c, 0x00000000, - 0x00000060, 0x60607c66, 0x6666667c, 0x00000000, - 0x00000000, 0x00007cc6, 0xfe06c67c, 0x00000000, - 0x00000078, 0x0c0c0c3e, 0x0c0c0c0c, 0x00000000, - 0x00000000, 0x00007c66, 0x6666667c, 0x60603e00, - 0x0000000c, 0x0c0c7ccc, 0xcccccccc, 0x00000000, - 0x00000030, 0x30003830, 0x30303078, 0x00000000, - 0x00000030, 0x30003c30, 0x30303030, 0x30301f00, - 0x0000000c, 0x0c0ccc6c, 0x3c6ccccc, 0x00000000, - 0x00000030, 0x30303030, 0x30303030, 0x00000000, - 0x00000000, 0x000066fe, 0xd6d6d6d6, 0x00000000, - 0x00000000, 0x000078cc, 0xcccccccc, 0x00000000, - 0x00000000, 0x00007cc6, 0xc6c6c67c, 0x00000000, - 0x00000000, 0x00007ccc, 0xcccccc7c, 0x0c0c0c00, - 0x00000000, 0x00007c66, 0x6666667c, 0x60606000, - 0x00000000, 0x000076dc, 0x0c0c0c0c, 0x00000000, - 0x00000000, 0x00007cc6, 0x1c70c67c, 0x00000000, - 0x00000000, 0x1818fe18, 0x18181870, 0x00000000, - 0x00000000, 0x00006666, 0x6666663c, 0x00000000, - 0x00000000, 0x0000c6c6, 0xc66c3810, 0x00000000, - 0x00000000, 0x0000c6d6, 0xd6d6fe6c, 0x00000000, - 0x00000000, 0x0000c66c, 0x38386cc6, 0x00000000, - 0x00000000, 0x00006666, 0x6666667c, 0x60603e00, - 0x00000000, 0x0000fe60, 0x30180cfe, 0x00000000, - 0x00000070, 0x1818180e, 0x18181870, 0x00000000, - 0x00000018, 0x18181800, 0x18181818, 0x00000000, - 0x0000000e, 0x18181870, 0x1818180e, 0x00000000, - 0x000000dc, 0x76000000, 0x00000000, 0x00000000, - 0x00000000, 0x0010386c, 0xc6c6fe00, 0x00000000 -}; - -#endif /* __SOLO6X10_OSD_FONT_H */ diff --git a/drivers/staging/solo6x10/p2m.c b/drivers/staging/solo6x10/p2m.c deleted file mode 100644 index 56210f0fc5ec..000000000000 --- a/drivers/staging/solo6x10/p2m.c +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" - -/* #define SOLO_TEST_P2M */ - -int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, - void *sys_addr, u32 ext_addr, u32 size) -{ - dma_addr_t dma_addr; - int ret; - - WARN_ON(!size); - BUG_ON(id >= SOLO_NR_P2M); - - if (!size) - return -EINVAL; - - dma_addr = pci_map_single(solo_dev->pdev, sys_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - ret = solo_p2m_dma_t(solo_dev, id, wr, dma_addr, ext_addr, size); - - pci_unmap_single(solo_dev->pdev, dma_addr, size, - wr ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE); - - return ret; -} - -int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size) -{ - struct p2m_desc *desc = kzalloc(sizeof(*desc) * 2, GFP_DMA); - int ret; - - if (desc == NULL) - return -ENOMEM; - - solo_p2m_push_desc(&desc[1], wr, dma_addr, ext_addr, size, 0, 0); - ret = solo_p2m_dma_desc(solo_dev, id, desc, 2); - kfree(desc); - - return ret; -} - -void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, - u32 ext_addr, u32 size, int repeat, u32 ext_size) -{ - desc->ta = cpu_to_le32(dma_addr); - desc->fa = cpu_to_le32(ext_addr); - - desc->ext = cpu_to_le32(SOLO_P2M_COPY_SIZE(size >> 2)); - desc->ctrl = cpu_to_le32(SOLO_P2M_BURST_SIZE(SOLO_P2M_BURST_256) | - (wr ? SOLO_P2M_WRITE : 0) | SOLO_P2M_TRANS_ON); - - /* Ext size only matters when we're repeating */ - if (repeat) { - desc->ext |= cpu_to_le32(SOLO_P2M_EXT_INC(ext_size >> 2)); - desc->ctrl |= cpu_to_le32(SOLO_P2M_PCI_INC(size >> 2) | - SOLO_P2M_REPEAT(repeat)); - } -} - -int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *desc, int desc_count) -{ - struct solo_p2m_dev *p2m_dev; - unsigned int timeout; - int ret = 0; - u32 config = 0; - dma_addr_t desc_dma = 0; - - BUG_ON(id >= SOLO_NR_P2M); - BUG_ON(!desc_count || desc_count > SOLO_NR_P2M_DESC); - - p2m_dev = &solo_dev->p2m_dev[id]; - - mutex_lock(&p2m_dev->mutex); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - - INIT_COMPLETION(p2m_dev->completion); - p2m_dev->error = 0; - - /* Enable the descriptors */ - config = solo_reg_read(solo_dev, SOLO_P2M_CONFIG(id)); - desc_dma = pci_map_single(solo_dev->pdev, desc, - desc_count * sizeof(*desc), - PCI_DMA_TODEVICE); - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), desc_dma); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), desc_count - 1); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config | - SOLO_P2M_DESC_MODE); - - /* Should have all descriptors completed from one interrupt */ - timeout = wait_for_completion_timeout(&p2m_dev->completion, HZ); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(id), 0); - - /* Reset back to non-descriptor mode */ - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(id), config); - solo_reg_write(solo_dev, SOLO_P2M_DESC_ID(id), 0); - solo_reg_write(solo_dev, SOLO_P2M_DES_ADR(id), 0); - pci_unmap_single(solo_dev->pdev, desc_dma, - desc_count * sizeof(*desc), - PCI_DMA_TODEVICE); - - if (p2m_dev->error) - ret = -EIO; - else if (timeout == 0) - ret = -EAGAIN; - - mutex_unlock(&p2m_dev->mutex); - - WARN_ON_ONCE(ret); - - return ret; -} - -int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *pdesc, int wr, - struct scatterlist *sg, u32 sg_off, - u32 ext_addr, u32 size) -{ - int i; - int idx; - - BUG_ON(id >= SOLO_NR_P2M); - - if (WARN_ON_ONCE(!size)) - return -EINVAL; - - memset(pdesc, 0, sizeof(*pdesc)); - - /* Should rewrite this to handle > SOLO_NR_P2M_DESC transactions */ - for (i = 0, idx = 1; idx < SOLO_NR_P2M_DESC && sg && size > 0; - i++, sg = sg_next(sg)) { - struct p2m_desc *desc = &pdesc[idx]; - u32 sg_len = sg_dma_len(sg); - u32 len; - - if (sg_off >= sg_len) { - sg_off -= sg_len; - continue; - } - - sg_len -= sg_off; - len = min(sg_len, size); - - solo_p2m_push_desc(desc, wr, sg_dma_address(sg) + sg_off, - ext_addr, len, 0, 0); - - size -= len; - ext_addr += len; - idx++; - - sg_off = 0; - } - - WARN_ON_ONCE(size || i >= SOLO_NR_P2M_DESC); - - return solo_p2m_dma_desc(solo_dev, id, pdesc, idx); -} - -#ifdef SOLO_TEST_P2M - -#define P2M_TEST_CHAR 0xbe - -static unsigned long long p2m_test(struct solo_dev *solo_dev, u8 id, - u32 base, int size) -{ - u8 *wr_buf; - u8 *rd_buf; - int i; - unsigned long long err_cnt = 0; - - wr_buf = kmalloc(size, GFP_KERNEL); - if (!wr_buf) { - printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); - return size; - } - - rd_buf = kmalloc(size, GFP_KERNEL); - if (!rd_buf) { - printk(SOLO6X10_NAME ": Failed to malloc for p2m_test\n"); - kfree(wr_buf); - return size; - } - - memset(wr_buf, P2M_TEST_CHAR, size); - memset(rd_buf, P2M_TEST_CHAR + 1, size); - - solo_p2m_dma(solo_dev, id, 1, wr_buf, base, size); - solo_p2m_dma(solo_dev, id, 0, rd_buf, base, size); - - for (i = 0; i < size; i++) - if (wr_buf[i] != rd_buf[i]) - err_cnt++; - - kfree(wr_buf); - kfree(rd_buf); - - return err_cnt; -} - -#define TEST_CHUNK_SIZE (8 * 1024) - -static void run_p2m_test(struct solo_dev *solo_dev) -{ - unsigned long long errs = 0; - u32 size = SOLO_JPEG_EXT_ADDR(solo_dev) + SOLO_JPEG_EXT_SIZE(solo_dev); - int i, d; - - printk(KERN_WARNING "%s: Testing %u bytes of external ram\n", - SOLO6X10_NAME, size); - - for (i = 0; i < size; i += TEST_CHUNK_SIZE) - for (d = 0; d < 4; d++) - errs += p2m_test(solo_dev, d, i, TEST_CHUNK_SIZE); - - printk(KERN_WARNING "%s: Found %llu errors during p2m test\n", - SOLO6X10_NAME, errs); - - return; -} -#else -#define run_p2m_test(__solo) do {} while (0) -#endif - -void solo_p2m_isr(struct solo_dev *solo_dev, int id) -{ - struct solo_p2m_dev *p2m_dev = &solo_dev->p2m_dev[id]; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_P2M(id)); - - complete(&p2m_dev->completion); -} - -void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - if (!(status & SOLO_PCI_ERR_P2M)) - return; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - p2m_dev->error = 1; - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - complete(&p2m_dev->completion); - } -} - -void solo_p2m_exit(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) - solo_irq_off(solo_dev, SOLO_IRQ_P2M(i)); -} - -int solo_p2m_init(struct solo_dev *solo_dev) -{ - struct solo_p2m_dev *p2m_dev; - int i; - - for (i = 0; i < SOLO_NR_P2M; i++) { - p2m_dev = &solo_dev->p2m_dev[i]; - - mutex_init(&p2m_dev->mutex); - init_completion(&p2m_dev->completion); - - solo_reg_write(solo_dev, SOLO_P2M_CONTROL(i), 0); - solo_reg_write(solo_dev, SOLO_P2M_CONFIG(i), - SOLO_P2M_CSC_16BIT_565 | - SOLO_P2M_DMA_INTERVAL(3) | - SOLO_P2M_DESC_INTR_OPT | - SOLO_P2M_PCI_MASTER_MODE); - solo_irq_on(solo_dev, SOLO_IRQ_P2M(i)); - } - - run_p2m_test(solo_dev); - - return 0; -} diff --git a/drivers/staging/solo6x10/registers.h b/drivers/staging/solo6x10/registers.h deleted file mode 100644 index aca544472c93..000000000000 --- a/drivers/staging/solo6x10/registers.h +++ /dev/null @@ -1,637 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 __SOLO6X10_REGISTERS_H -#define __SOLO6X10_REGISTERS_H - -#include "offsets.h" - -/* Global 6X10 system configuration */ -#define SOLO_SYS_CFG 0x0000 -#define SOLO6010_SYS_CFG_FOUT_EN 0x00000001 /* 6010 only */ -#define SOLO6010_SYS_CFG_PLL_BYPASS 0x00000002 /* 6010 only */ -#define SOLO6010_SYS_CFG_PLL_PWDN 0x00000004 /* 6010 only */ -#define SOLO6010_SYS_CFG_OUTDIV(__n) (((__n) & 0x003) << 3) /* 6010 only */ -#define SOLO6010_SYS_CFG_FEEDBACKDIV(__n) (((__n) & 0x1ff) << 5) /* 6010 only */ -#define SOLO6010_SYS_CFG_INPUTDIV(__n) (((__n) & 0x01f) << 14) /* 6010 only */ -#define SOLO_SYS_CFG_CLOCK_DIV 0x00080000 -#define SOLO_SYS_CFG_NCLK_DELAY(__n) (((__n) & 0x003) << 24) -#define SOLO_SYS_CFG_PCLK_DELAY(__n) (((__n) & 0x00f) << 26) -#define SOLO_SYS_CFG_SDRAM64BIT 0x40000000 /* 6110: must be set */ -#define SOLO_SYS_CFG_RESET 0x80000000 - -#define SOLO_DMA_CTRL 0x0004 -#define SOLO_DMA_CTRL_REFRESH_CYCLE(n) ((n)<<8) -/* 0=16/32MB, 1=32/64MB, 2=64/128MB, 3=128/256MB */ -#define SOLO_DMA_CTRL_SDRAM_SIZE(n) ((n)<<6) -#define SOLO_DMA_CTRL_SDRAM_CLK_INVERT (1<<5) -#define SOLO_DMA_CTRL_STROBE_SELECT (1<<4) -#define SOLO_DMA_CTRL_READ_DATA_SELECT (1<<3) -#define SOLO_DMA_CTRL_READ_CLK_SELECT (1<<2) -#define SOLO_DMA_CTRL_LATENCY(n) ((n)<<0) -#define SOLO_DMA_CTRL1 0x0008 - -#define SOLO_SYS_VCLK 0x000C -#define SOLO_VCLK_INVERT (1<<22) -/* 0=sys_clk/4, 1=sys_clk/2, 2=clk_in/2 of system input */ -#define SOLO_VCLK_SELECT(n) ((n)<<20) -#define SOLO_VCLK_VIN1415_DELAY(n) ((n)<<14) -#define SOLO_VCLK_VIN1213_DELAY(n) ((n)<<12) -#define SOLO_VCLK_VIN1011_DELAY(n) ((n)<<10) -#define SOLO_VCLK_VIN0809_DELAY(n) ((n)<<8) -#define SOLO_VCLK_VIN0607_DELAY(n) ((n)<<6) -#define SOLO_VCLK_VIN0405_DELAY(n) ((n)<<4) -#define SOLO_VCLK_VIN0203_DELAY(n) ((n)<<2) -#define SOLO_VCLK_VIN0001_DELAY(n) ((n)<<0) - -#define SOLO_IRQ_STAT 0x0010 -#define SOLO_IRQ_ENABLE 0x0014 -#define SOLO_IRQ_P2M(n) (1<<((n)+17)) -#define SOLO_IRQ_GPIO (1<<16) -#define SOLO_IRQ_VIDEO_LOSS (1<<15) -#define SOLO_IRQ_VIDEO_IN (1<<14) -#define SOLO_IRQ_MOTION (1<<13) -#define SOLO_IRQ_ATA_CMD (1<<12) -#define SOLO_IRQ_ATA_DIR (1<<11) -#define SOLO_IRQ_PCI_ERR (1<<10) -#define SOLO_IRQ_PS2_1 (1<<9) -#define SOLO_IRQ_PS2_0 (1<<8) -#define SOLO_IRQ_SPI (1<<7) -#define SOLO_IRQ_IIC (1<<6) -#define SOLO_IRQ_UART(n) (1<<((n) + 4)) -#define SOLO_IRQ_G723 (1<<3) -#define SOLO_IRQ_DECODER (1<<1) -#define SOLO_IRQ_ENCODER (1<<0) - -#define SOLO_CHIP_OPTION 0x001C -#define SOLO_CHIP_ID_MASK 0x00000007 - -#define SOLO6110_PLL_CONFIG 0x0020 -#define SOLO6110_PLL_RANGE_BYPASS (0 << 20) -#define SOLO6110_PLL_RANGE_5_10MHZ (1 << 20) -#define SOLO6110_PLL_RANGE_8_16MHZ (2 << 20) -#define SOLO6110_PLL_RANGE_13_26MHZ (3 << 20) -#define SOLO6110_PLL_RANGE_21_42MHZ (4 << 20) -#define SOLO6110_PLL_RANGE_34_68MHZ (5 << 20) -#define SOLO6110_PLL_RANGE_54_108MHZ (6 << 20) -#define SOLO6110_PLL_RANGE_88_200MHZ (7 << 20) -#define SOLO6110_PLL_DIVR(x) (((x) - 1) << 15) -#define SOLO6110_PLL_DIVQ_EXP(x) ((x) << 12) -#define SOLO6110_PLL_DIVF(x) (((x) - 1) << 4) -#define SOLO6110_PLL_RESET (1 << 3) -#define SOLO6110_PLL_BYPASS (1 << 2) -#define SOLO6110_PLL_FSEN (1 << 1) -#define SOLO6110_PLL_FB (1 << 0) - -#define SOLO_EEPROM_CTRL 0x0060 -#define SOLO_EEPROM_ACCESS_EN (1<<7) -#define SOLO_EEPROM_CS (1<<3) -#define SOLO_EEPROM_CLK (1<<2) -#define SOLO_EEPROM_DO (1<<1) -#define SOLO_EEPROM_DI (1<<0) -#define SOLO_EEPROM_ENABLE (EEPROM_ACCESS_EN | EEPROM_CS) - -#define SOLO_PCI_ERR 0x0070 -#define SOLO_PCI_ERR_FATAL 0x00000001 -#define SOLO_PCI_ERR_PARITY 0x00000002 -#define SOLO_PCI_ERR_TARGET 0x00000004 -#define SOLO_PCI_ERR_TIMEOUT 0x00000008 -#define SOLO_PCI_ERR_P2M 0x00000010 -#define SOLO_PCI_ERR_ATA 0x00000020 -#define SOLO_PCI_ERR_P2M_DESC 0x00000040 -#define SOLO_PCI_ERR_FSM0(__s) (((__s) >> 16) & 0x0f) -#define SOLO_PCI_ERR_FSM1(__s) (((__s) >> 20) & 0x0f) -#define SOLO_PCI_ERR_FSM2(__s) (((__s) >> 24) & 0x1f) - -#define SOLO_P2M_BASE 0x0080 - -#define SOLO_P2M_CONFIG(n) (0x0080 + ((n)*0x20)) -#define SOLO_P2M_DMA_INTERVAL(n) ((n)<<6)/* N*32 clocks */ -#define SOLO_P2M_CSC_BYTE_REORDER (1<<5) /* BGR -> RGB */ -/* 0:r=[14:10] g=[9:5] b=[4:0], 1:r=[15:11] g=[10:5] b=[4:0] */ -#define SOLO_P2M_CSC_16BIT_565 (1<<4) -#define SOLO_P2M_UV_SWAP (1<<3) -#define SOLO_P2M_PCI_MASTER_MODE (1<<2) -#define SOLO_P2M_DESC_INTR_OPT (1<<1) /* 1:Empty, 0:Each */ -#define SOLO_P2M_DESC_MODE (1<<0) - -#define SOLO_P2M_DES_ADR(n) (0x0084 + ((n)*0x20)) - -#define SOLO_P2M_DESC_ID(n) (0x0088 + ((n)*0x20)) -#define SOLO_P2M_UPDATE_ID(n) ((n)<<0) - -#define SOLO_P2M_STATUS(n) (0x008C + ((n)*0x20)) -#define SOLO_P2M_COMMAND_DONE (1<<8) -#define SOLO_P2M_CURRENT_ID(stat) (0xff & (stat)) - -#define SOLO_P2M_CONTROL(n) (0x0090 + ((n)*0x20)) -#define SOLO_P2M_PCI_INC(n) ((n)<<20) -#define SOLO_P2M_REPEAT(n) ((n)<<10) -/* 0:512, 1:256, 2:128, 3:64, 4:32, 5:128(2page) */ -#define SOLO_P2M_BURST_SIZE(n) ((n)<<7) -#define SOLO_P2M_BURST_512 0 -#define SOLO_P2M_BURST_256 1 -#define SOLO_P2M_BURST_128 2 -#define SOLO_P2M_BURST_64 3 -#define SOLO_P2M_BURST_32 4 -#define SOLO_P2M_CSC_16BIT (1<<6) /* 0:24bit, 1:16bit */ -/* 0:Y[0]<-0(OFF), 1:Y[0]<-1(ON), 2:Y[0]<-G[0], 3:Y[0]<-Bit[15] */ -#define SOLO_P2M_ALPHA_MODE(n) ((n)<<4) -#define SOLO_P2M_CSC_ON (1<<3) -#define SOLO_P2M_INTERRUPT_REQ (1<<2) -#define SOLO_P2M_WRITE (1<<1) -#define SOLO_P2M_TRANS_ON (1<<0) - -#define SOLO_P2M_EXT_CFG(n) (0x0094 + ((n)*0x20)) -#define SOLO_P2M_EXT_INC(n) ((n)<<20) -#define SOLO_P2M_COPY_SIZE(n) ((n)<<0) - -#define SOLO_P2M_TAR_ADR(n) (0x0098 + ((n)*0x20)) - -#define SOLO_P2M_EXT_ADR(n) (0x009C + ((n)*0x20)) - -#define SOLO_P2M_BUFFER(i) (0x2000 + ((i)*4)) - -#define SOLO_VI_CH_SWITCH_0 0x0100 -#define SOLO_VI_CH_SWITCH_1 0x0104 -#define SOLO_VI_CH_SWITCH_2 0x0108 - -#define SOLO_VI_CH_ENA 0x010C -#define SOLO_VI_CH_FORMAT 0x0110 -#define SOLO_VI_FD_SEL_MASK(n) ((n)<<16) -#define SOLO_VI_PROG_MASK(n) ((n)<<0) - -#define SOLO_VI_FMT_CFG 0x0114 -#define SOLO_VI_FMT_CHECK_VCOUNT (1<<31) -#define SOLO_VI_FMT_CHECK_HCOUNT (1<<30) -#define SOLO_VI_FMT_TEST_SIGNAL (1<<28) - -#define SOLO_VI_PAGE_SW 0x0118 -#define SOLO_FI_INV_DISP_LIVE(n) ((n)<<8) -#define SOLO_FI_INV_DISP_OUT(n) ((n)<<7) -#define SOLO_DISP_SYNC_FI(n) ((n)<<6) -#define SOLO_PIP_PAGE_ADD(n) ((n)<<3) -#define SOLO_NORMAL_PAGE_ADD(n) ((n)<<0) - -#define SOLO_VI_ACT_I_P 0x011C -#define SOLO_VI_ACT_I_S 0x0120 -#define SOLO_VI_ACT_P 0x0124 -#define SOLO_VI_FI_INVERT (1<<31) -#define SOLO_VI_H_START(n) ((n)<<21) -#define SOLO_VI_V_START(n) ((n)<<11) -#define SOLO_VI_V_STOP(n) ((n)<<0) - -#define SOLO_VI_STATUS0 0x0128 -#define SOLO_VI_STATUS0_PAGE(__n) ((__n) & 0x07) -#define SOLO_VI_STATUS1 0x012C - -/* XXX: Might be better off in kernel level disp.h */ -#define DISP_PAGE(stat) ((stat) & 0x07) - -#define SOLO_VI_PB_CONFIG 0x0130 -#define SOLO_VI_PB_USER_MODE (1<<1) -#define SOLO_VI_PB_PAL (1<<0) -#define SOLO_VI_PB_RANGE_HV 0x0134 -#define SOLO_VI_PB_HSIZE(h) ((h)<<12) -#define SOLO_VI_PB_VSIZE(v) ((v)<<0) -#define SOLO_VI_PB_ACT_H 0x0138 -#define SOLO_VI_PB_HSTART(n) ((n)<<12) -#define SOLO_VI_PB_HSTOP(n) ((n)<<0) -#define SOLO_VI_PB_ACT_V 0x013C -#define SOLO_VI_PB_VSTART(n) ((n)<<12) -#define SOLO_VI_PB_VSTOP(n) ((n)<<0) - -#define SOLO_VI_MOSAIC(ch) (0x0140 + ((ch)*4)) -#define SOLO_VI_MOSAIC_SX(x) ((x)<<24) -#define SOLO_VI_MOSAIC_EX(x) ((x)<<16) -#define SOLO_VI_MOSAIC_SY(x) ((x)<<8) -#define SOLO_VI_MOSAIC_EY(x) ((x)<<0) - -#define SOLO_VI_WIN_CTRL0(ch) (0x0180 + ((ch)*4)) -#define SOLO_VI_WIN_CTRL1(ch) (0x01C0 + ((ch)*4)) - -#define SOLO_VI_WIN_CHANNEL(n) ((n)<<28) - -#define SOLO_VI_WIN_PIP(n) ((n)<<27) -#define SOLO_VI_WIN_SCALE(n) ((n)<<24) - -#define SOLO_VI_WIN_SX(x) ((x)<<12) -#define SOLO_VI_WIN_EX(x) ((x)<<0) - -#define SOLO_VI_WIN_SY(x) ((x)<<12) -#define SOLO_VI_WIN_EY(x) ((x)<<0) - -#define SOLO_VI_WIN_ON(ch) (0x0200 + ((ch)*4)) - -#define SOLO_VI_WIN_SW 0x0240 -#define SOLO_VI_WIN_LIVE_AUTO_MUTE 0x0244 - -#define SOLO_VI_MOT_ADR 0x0260 -#define SOLO_VI_MOTION_EN(mask) ((mask)<<16) -#define SOLO_VI_MOT_CTRL 0x0264 -#define SOLO_VI_MOTION_FRAME_COUNT(n) ((n)<<24) -#define SOLO_VI_MOTION_SAMPLE_LENGTH(n) ((n)<<16) -#define SOLO_VI_MOTION_INTR_START_STOP (1<<15) -#define SOLO_VI_MOTION_FREEZE_DATA (1<<14) -#define SOLO_VI_MOTION_SAMPLE_COUNT(n) ((n)<<0) -#define SOLO_VI_MOT_CLEAR 0x0268 -#define SOLO_VI_MOT_STATUS 0x026C -#define SOLO_VI_MOTION_CNT(n) ((n)<<0) -#define SOLO_VI_MOTION_BORDER 0x0270 -#define SOLO_VI_MOTION_BAR 0x0274 -#define SOLO_VI_MOTION_Y_SET (1<<29) -#define SOLO_VI_MOTION_Y_ADD (1<<28) -#define SOLO_VI_MOTION_CB_SET (1<<27) -#define SOLO_VI_MOTION_CB_ADD (1<<26) -#define SOLO_VI_MOTION_CR_SET (1<<25) -#define SOLO_VI_MOTION_CR_ADD (1<<24) -#define SOLO_VI_MOTION_Y_VALUE(v) ((v)<<16) -#define SOLO_VI_MOTION_CB_VALUE(v) ((v)<<8) -#define SOLO_VI_MOTION_CR_VALUE(v) ((v)<<0) - -#define SOLO_VO_FMT_ENC 0x0300 -#define SOLO_VO_SCAN_MODE_PROGRESSIVE (1<<31) -#define SOLO_VO_FMT_TYPE_PAL (1<<30) -#define SOLO_VO_FMT_TYPE_NTSC 0 -#define SOLO_VO_USER_SET (1<<29) - -#define SOLO_VO_FI_CHANGE (1<<20) -#define SOLO_VO_USER_COLOR_SET_VSYNC (1<<19) -#define SOLO_VO_USER_COLOR_SET_HSYNC (1<<18) -#define SOLO_VO_USER_COLOR_SET_NAV (1<<17) -#define SOLO_VO_USER_COLOR_SET_NAH (1<<16) -#define SOLO_VO_NA_COLOR_Y(Y) ((Y)<<8) -#define SOLO_VO_NA_COLOR_CB(CB) (((CB)/16)<<4) -#define SOLO_VO_NA_COLOR_CR(CR) (((CR)/16)<<0) - -#define SOLO_VO_ACT_H 0x0304 -#define SOLO_VO_H_BLANK(n) ((n)<<22) -#define SOLO_VO_H_START(n) ((n)<<11) -#define SOLO_VO_H_STOP(n) ((n)<<0) - -#define SOLO_VO_ACT_V 0x0308 -#define SOLO_VO_V_BLANK(n) ((n)<<22) -#define SOLO_VO_V_START(n) ((n)<<11) -#define SOLO_VO_V_STOP(n) ((n)<<0) - -#define SOLO_VO_RANGE_HV 0x030C -#define SOLO_VO_SYNC_INVERT (1<<24) -#define SOLO_VO_HSYNC_INVERT (1<<23) -#define SOLO_VO_VSYNC_INVERT (1<<22) -#define SOLO_VO_H_LEN(n) ((n)<<11) -#define SOLO_VO_V_LEN(n) ((n)<<0) - -#define SOLO_VO_DISP_CTRL 0x0310 -#define SOLO_VO_DISP_ON (1<<31) -#define SOLO_VO_DISP_ERASE_COUNT(n) ((n&0xf)<<24) -#define SOLO_VO_DISP_DOUBLE_SCAN (1<<22) -#define SOLO_VO_DISP_SINGLE_PAGE (1<<21) -#define SOLO_VO_DISP_BASE(n) (((n)>>16) & 0xffff) - -#define SOLO_VO_DISP_ERASE 0x0314 -#define SOLO_VO_DISP_ERASE_ON (1<<0) - -#define SOLO_VO_ZOOM_CTRL 0x0318 -#define SOLO_VO_ZOOM_VER_ON (1<<24) -#define SOLO_VO_ZOOM_HOR_ON (1<<23) -#define SOLO_VO_ZOOM_V_COMP (1<<22) -#define SOLO_VO_ZOOM_SX(h) (((h)/2)<<11) -#define SOLO_VO_ZOOM_SY(v) (((v)/2)<<0) - -#define SOLO_VO_FREEZE_CTRL 0x031C -#define SOLO_VO_FREEZE_ON (1<<1) -#define SOLO_VO_FREEZE_INTERPOLATION (1<<0) - -#define SOLO_VO_BKG_COLOR 0x0320 -#define SOLO_BG_Y(y) ((y)<<16) -#define SOLO_BG_U(u) ((u)<<8) -#define SOLO_BG_V(v) ((v)<<0) - -#define SOLO_VO_DEINTERLACE 0x0324 -#define SOLO_VO_DEINTERLACE_THRESHOLD(n) ((n)<<8) -#define SOLO_VO_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) - -#define SOLO_VO_BORDER_LINE_COLOR 0x0330 -#define SOLO_VO_BORDER_FILL_COLOR 0x0334 -#define SOLO_VO_BORDER_LINE_MASK 0x0338 -#define SOLO_VO_BORDER_FILL_MASK 0x033c - -#define SOLO_VO_BORDER_X(n) (0x0340+((n)*4)) -#define SOLO_VO_BORDER_Y(n) (0x0354+((n)*4)) - -#define SOLO_VO_CELL_EXT_SET 0x0368 -#define SOLO_VO_CELL_EXT_START 0x036c -#define SOLO_VO_CELL_EXT_STOP 0x0370 - -#define SOLO_VO_CELL_EXT_SET2 0x0374 -#define SOLO_VO_CELL_EXT_START2 0x0378 -#define SOLO_VO_CELL_EXT_STOP2 0x037c - -#define SOLO_VO_RECTANGLE_CTRL(n) (0x0368+((n)*12)) -#define SOLO_VO_RECTANGLE_START(n) (0x036c+((n)*12)) -#define SOLO_VO_RECTANGLE_STOP(n) (0x0370+((n)*12)) - -#define SOLO_VO_CURSOR_POS (0x0380) -#define SOLO_VO_CURSOR_CLR (0x0384) -#define SOLO_VO_CURSOR_CLR2 (0x0388) -#define SOLO_VO_CURSOR_MASK(id) (0x0390+((id)*4)) - -#define SOLO_VO_EXPANSION(id) (0x0250+((id)*4)) - -#define SOLO_OSG_CONFIG 0x03E0 -#define SOLO_VO_OSG_ON (1<<31) -#define SOLO_VO_OSG_COLOR_MUTE (1<<28) -#define SOLO_VO_OSG_ALPHA_RATE(n) ((n)<<22) -#define SOLO_VO_OSG_ALPHA_BG_RATE(n) ((n)<<16) -#define SOLO_VO_OSG_BASE(offset) (((offset)>>16)&0xffff) - -#define SOLO_OSG_ERASE 0x03E4 -#define SOLO_OSG_ERASE_ON (0x80) -#define SOLO_OSG_ERASE_OFF (0x00) - -#define SOLO_VO_OSG_BLINK 0x03E8 -#define SOLO_VO_OSG_BLINK_ON (1<<1) -#define SOLO_VO_OSG_BLINK_INTREVAL18 (1<<0) - -#define SOLO_CAP_BASE 0x0400 -#define SOLO_CAP_MAX_PAGE(n) ((n)<<16) -#define SOLO_CAP_BASE_ADDR(n) ((n)<<0) -#define SOLO_CAP_BTW 0x0404 -#define SOLO_CAP_PROG_BANDWIDTH(n) ((n)<<8) -#define SOLO_CAP_MAX_BANDWIDTH(n) ((n)<<0) - -#define SOLO_DIM_SCALE1 0x0408 -#define SOLO_DIM_SCALE2 0x040C -#define SOLO_DIM_SCALE3 0x0410 -#define SOLO_DIM_SCALE4 0x0414 -#define SOLO_DIM_SCALE5 0x0418 -#define SOLO_DIM_V_MB_NUM_FRAME(n) ((n)<<16) -#define SOLO_DIM_V_MB_NUM_FIELD(n) ((n)<<8) -#define SOLO_DIM_H_MB_NUM(n) ((n)<<0) - -#define SOLO_DIM_PROG 0x041C -#define SOLO_CAP_STATUS 0x0420 - -#define SOLO_CAP_CH_SCALE(ch) (0x0440+((ch)*4)) -#define SOLO_CAP_CH_COMP_ENA_E(ch) (0x0480+((ch)*4)) -#define SOLO_CAP_CH_INTV(ch) (0x04C0+((ch)*4)) -#define SOLO_CAP_CH_INTV_E(ch) (0x0500+((ch)*4)) - - -#define SOLO_VE_CFG0 0x0610 -#define SOLO_VE_TWO_PAGE_MODE (1<<31) -#define SOLO_VE_INTR_CTRL(n) ((n)<<24) -#define SOLO_VE_BLOCK_SIZE(n) ((n)<<16) -#define SOLO_VE_BLOCK_BASE(n) ((n)<<0) - -#define SOLO_VE_CFG1 0x0614 -#define SOLO6110_VE_MPEG_SIZE_H(n) ((n)<<28) /* 6110 only */ -#define SOLO6010_VE_BYTE_ALIGN(n) ((n)<<24) /* 6010 only */ -#define SOLO6110_VE_JPEG_SIZE_H(n) ((n)<<20) /* 6110 only */ -#define SOLO_VE_INSERT_INDEX (1<<18) -#define SOLO_VE_MOTION_MODE(n) ((n)<<16) -#define SOLO_VE_MOTION_BASE(n) ((n)<<0) - -#define SOLO_VE_WMRK_POLY 0x061C -#define SOLO_VE_VMRK_INIT_KEY 0x0620 -#define SOLO_VE_WMRK_STRL 0x0624 -#define SOLO_VE_ENCRYP_POLY 0x0628 -#define SOLO_VE_ENCRYP_INIT 0x062C -#define SOLO_VE_ATTR 0x0630 -#define SOLO_VE_LITTLE_ENDIAN (1<<31) -#define SOLO_COMP_ATTR_RN (1<<30) -#define SOLO_COMP_ATTR_FCODE(n) ((n)<<27) -#define SOLO_COMP_TIME_INC(n) ((n)<<25) -#define SOLO_COMP_TIME_WIDTH(n) ((n)<<21) -#define SOLO_DCT_INTERVAL(n) ((n)<<16) - -#define SOLO_VE_STATE(n) (0x0640+((n)*4)) - -#define SOLO_VE_JPEG_QP_TBL 0x0670 -#define SOLO_VE_JPEG_QP_CH_L 0x0674 -#define SOLO_VE_JPEG_QP_CH_H 0x0678 -#define SOLO_VE_JPEG_CFG 0x067C -#define SOLO_VE_JPEG_CTRL 0x0680 - -#define SOLO_VE_OSD_CH 0x0690 -#define SOLO_VE_OSD_BASE 0x0694 -#define SOLO_VE_OSD_CLR 0x0698 -#define SOLO_VE_OSD_OPT 0x069C - -#define SOLO_VE_CH_INTL(ch) (0x0700+((ch)*4)) -#define SOLO6010_VE_CH_MOT(ch) (0x0740+((ch)*4)) /* 6010 only */ -#define SOLO_VE_CH_QP(ch) (0x0780+((ch)*4)) -#define SOLO_VE_CH_QP_E(ch) (0x07C0+((ch)*4)) -#define SOLO_VE_CH_GOP(ch) (0x0800+((ch)*4)) -#define SOLO_VE_CH_GOP_E(ch) (0x0840+((ch)*4)) -#define SOLO_VE_CH_REF_BASE(ch) (0x0880+((ch)*4)) -#define SOLO_VE_CH_REF_BASE_E(ch) (0x08C0+((ch)*4)) - -#define SOLO_VE_MPEG4_QUE(n) (0x0A00+((n)*8)) -#define SOLO_VE_JPEG_QUE(n) (0x0A04+((n)*8)) - -#define SOLO_VD_CFG0 0x0900 -#define SOLO6010_VD_CFG_NO_WRITE_NO_WINDOW (1<<24) /* 6010 only */ -#define SOLO_VD_CFG_BUSY_WIAT_CODE (1<<23) -#define SOLO_VD_CFG_BUSY_WIAT_REF (1<<22) -#define SOLO_VD_CFG_BUSY_WIAT_RES (1<<21) -#define SOLO_VD_CFG_BUSY_WIAT_MS (1<<20) -#define SOLO_VD_CFG_SINGLE_MODE (1<<18) -#define SOLO_VD_CFG_SCAL_MANUAL (1<<17) -#define SOLO_VD_CFG_USER_PAGE_CTRL (1<<16) -#define SOLO_VD_CFG_LITTLE_ENDIAN (1<<15) -#define SOLO_VD_CFG_START_FI (1<<14) -#define SOLO_VD_CFG_ERR_LOCK (1<<13) -#define SOLO_VD_CFG_ERR_INT_ENA (1<<12) -#define SOLO_VD_CFG_TIME_WIDTH(n) ((n)<<8) -#define SOLO_VD_CFG_DCT_INTERVAL(n) ((n)<<0) - -#define SOLO_VD_CFG1 0x0904 - -#define SOLO_VD_DEINTERLACE 0x0908 -#define SOLO_VD_DEINTERLACE_THRESHOLD(n) ((n)<<8) -#define SOLO_VD_DEINTERLACE_EDGE_VALUE(n) ((n)<<0) - -#define SOLO_VD_CODE_ADR 0x090C - -#define SOLO_VD_CTRL 0x0910 -#define SOLO_VD_OPER_ON (1<<31) -#define SOLO_VD_MAX_ITEM(n) ((n)<<0) - -#define SOLO_VD_STATUS0 0x0920 -#define SOLO_VD_STATUS0_INTR_ACK (1<<22) -#define SOLO_VD_STATUS0_INTR_EMPTY (1<<21) -#define SOLO_VD_STATUS0_INTR_ERR (1<<20) - -#define SOLO_VD_STATUS1 0x0924 - -#define SOLO_VD_IDX0 0x0930 -#define SOLO_VD_IDX_INTERLACE (1<<30) -#define SOLO_VD_IDX_CHANNEL(n) ((n)<<24) -#define SOLO_VD_IDX_SIZE(n) ((n)<<0) - -#define SOLO_VD_IDX1 0x0934 -#define SOLO_VD_IDX_SRC_SCALE(n) ((n)<<28) -#define SOLO_VD_IDX_WINDOW(n) ((n)<<24) -#define SOLO_VD_IDX_DEINTERLACE (1<<16) -#define SOLO_VD_IDX_H_BLOCK(n) ((n)<<8) -#define SOLO_VD_IDX_V_BLOCK(n) ((n)<<0) - -#define SOLO_VD_IDX2 0x0938 -#define SOLO_VD_IDX_REF_BASE_SIDE (1<<31) -#define SOLO_VD_IDX_REF_BASE(n) (((n)>>16)&0xffff) - -#define SOLO_VD_IDX3 0x093C -#define SOLO_VD_IDX_DISP_SCALE(n) ((n)<<28) -#define SOLO_VD_IDX_INTERLACE_WR (1<<27) -#define SOLO_VD_IDX_INTERPOL (1<<26) -#define SOLO_VD_IDX_HOR2X (1<<25) -#define SOLO_VD_IDX_OFFSET_X(n) ((n)<<12) -#define SOLO_VD_IDX_OFFSET_Y(n) ((n)<<0) - -#define SOLO_VD_IDX4 0x0940 -#define SOLO_VD_IDX_DEC_WR_PAGE(n) ((n)<<8) -#define SOLO_VD_IDX_DISP_RD_PAGE(n) ((n)<<0) - -#define SOLO_VD_WR_PAGE(n) (0x03F0 + ((n) * 4)) - - -#define SOLO_GPIO_CONFIG_0 0x0B00 -#define SOLO_GPIO_CONFIG_1 0x0B04 -#define SOLO_GPIO_DATA_OUT 0x0B08 -#define SOLO_GPIO_DATA_IN 0x0B0C -#define SOLO_GPIO_INT_ACK_STA 0x0B10 -#define SOLO_GPIO_INT_ENA 0x0B14 -#define SOLO_GPIO_INT_CFG_0 0x0B18 -#define SOLO_GPIO_INT_CFG_1 0x0B1C - - -#define SOLO_IIC_CFG 0x0B20 -#define SOLO_IIC_ENABLE (1<<8) -#define SOLO_IIC_PRESCALE(n) ((n)<<0) - -#define SOLO_IIC_CTRL 0x0B24 -#define SOLO_IIC_AUTO_CLEAR (1<<20) -#define SOLO_IIC_STATE_RX_ACK (1<<19) -#define SOLO_IIC_STATE_BUSY (1<<18) -#define SOLO_IIC_STATE_SIG_ERR (1<<17) -#define SOLO_IIC_STATE_TRNS (1<<16) -#define SOLO_IIC_CH_SET(n) ((n)<<5) -#define SOLO_IIC_ACK_EN (1<<4) -#define SOLO_IIC_START (1<<3) -#define SOLO_IIC_STOP (1<<2) -#define SOLO_IIC_READ (1<<1) -#define SOLO_IIC_WRITE (1<<0) - -#define SOLO_IIC_TXD 0x0B28 -#define SOLO_IIC_RXD 0x0B2C - -/* - * UART REGISTER - */ -#define SOLO_UART_CONTROL(n) (0x0BA0 + ((n)*0x20)) -#define SOLO_UART_CLK_DIV(n) ((n)<<24) -#define SOLO_MODEM_CTRL_EN (1<<20) -#define SOLO_PARITY_ERROR_DROP (1<<18) -#define SOLO_IRQ_ERR_EN (1<<17) -#define SOLO_IRQ_RX_EN (1<<16) -#define SOLO_IRQ_TX_EN (1<<15) -#define SOLO_RX_EN (1<<14) -#define SOLO_TX_EN (1<<13) -#define SOLO_UART_HALF_DUPLEX (1<<12) -#define SOLO_UART_LOOPBACK (1<<11) - -#define SOLO_BAUDRATE_230400 ((0<<9)|(0<<6)) -#define SOLO_BAUDRATE_115200 ((0<<9)|(1<<6)) -#define SOLO_BAUDRATE_57600 ((0<<9)|(2<<6)) -#define SOLO_BAUDRATE_38400 ((0<<9)|(3<<6)) -#define SOLO_BAUDRATE_19200 ((0<<9)|(4<<6)) -#define SOLO_BAUDRATE_9600 ((0<<9)|(5<<6)) -#define SOLO_BAUDRATE_4800 ((0<<9)|(6<<6)) -#define SOLO_BAUDRATE_2400 ((1<<9)|(6<<6)) -#define SOLO_BAUDRATE_1200 ((2<<9)|(6<<6)) -#define SOLO_BAUDRATE_300 ((3<<9)|(6<<6)) - -#define SOLO_UART_DATA_BIT_8 (3<<4) -#define SOLO_UART_DATA_BIT_7 (2<<4) -#define SOLO_UART_DATA_BIT_6 (1<<4) -#define SOLO_UART_DATA_BIT_5 (0<<4) - -#define SOLO_UART_STOP_BIT_1 (0<<2) -#define SOLO_UART_STOP_BIT_2 (1<<2) - -#define SOLO_UART_PARITY_NONE (0<<0) -#define SOLO_UART_PARITY_EVEN (2<<0) -#define SOLO_UART_PARITY_ODD (3<<0) - -#define SOLO_UART_STATUS(n) (0x0BA4 + ((n)*0x20)) -#define SOLO_UART_CTS (1<<15) -#define SOLO_UART_RX_BUSY (1<<14) -#define SOLO_UART_OVERRUN (1<<13) -#define SOLO_UART_FRAME_ERR (1<<12) -#define SOLO_UART_PARITY_ERR (1<<11) -#define SOLO_UART_TX_BUSY (1<<5) - -#define SOLO_UART_RX_BUFF_CNT(stat) (((stat)>>6) & 0x1f) -#define SOLO_UART_RX_BUFF_SIZE 8 -#define SOLO_UART_TX_BUFF_CNT(stat) (((stat)>>0) & 0x1f) -#define SOLO_UART_TX_BUFF_SIZE 8 - -#define SOLO_UART_TX_DATA(n) (0x0BA8 + ((n)*0x20)) -#define SOLO_UART_TX_DATA_PUSH (1<<8) -#define SOLO_UART_RX_DATA(n) (0x0BAC + ((n)*0x20)) -#define SOLO_UART_RX_DATA_POP (1<<8) - -#define SOLO_TIMER_CLOCK_NUM 0x0be0 -#define SOLO_TIMER_WATCHDOG 0x0be4 -#define SOLO_TIMER_USEC 0x0be8 -#define SOLO_TIMER_SEC 0x0bec - -#define SOLO_AUDIO_CONTROL 0x0D00 -#define SOLO_AUDIO_ENABLE (1<<31) -#define SOLO_AUDIO_MASTER_MODE (1<<30) -#define SOLO_AUDIO_I2S_MODE (1<<29) -#define SOLO_AUDIO_I2S_LR_SWAP (1<<27) -#define SOLO_AUDIO_I2S_8BIT (1<<26) -#define SOLO_AUDIO_I2S_MULTI(n) ((n)<<24) -#define SOLO_AUDIO_MIX_9TO0 (1<<23) -#define SOLO_AUDIO_DEC_9TO0_VOL(n) ((n)<<20) -#define SOLO_AUDIO_MIX_19TO10 (1<<19) -#define SOLO_AUDIO_DEC_19TO10_VOL(n) ((n)<<16) -#define SOLO_AUDIO_MODE(n) ((n)<<0) -#define SOLO_AUDIO_SAMPLE 0x0D04 -#define SOLO_AUDIO_EE_MODE_ON (1<<30) -#define SOLO_AUDIO_EE_ENC_CH(ch) ((ch)<<25) -#define SOLO_AUDIO_BITRATE(n) ((n)<<16) -#define SOLO_AUDIO_CLK_DIV(n) ((n)<<0) -#define SOLO_AUDIO_FDMA_INTR 0x0D08 -#define SOLO_AUDIO_FDMA_INTERVAL(n) ((n)<<19) -#define SOLO_AUDIO_INTR_ORDER(n) ((n)<<16) -#define SOLO_AUDIO_FDMA_BASE(n) ((n)<<0) -#define SOLO_AUDIO_EVOL_0 0x0D0C -#define SOLO_AUDIO_EVOL_1 0x0D10 -#define SOLO_AUDIO_EVOL(ch, value) ((value)<<((ch)%10)) -#define SOLO_AUDIO_STA 0x0D14 - - -#define SOLO_WATCHDOG 0x0BE4 -#define WATCHDOG_STAT(status) (status<<8) -#define WATCHDOG_TIME(sec) (sec&0xff) - -#endif /* __SOLO6X10_REGISTERS_H */ diff --git a/drivers/staging/solo6x10/solo6x10.h b/drivers/staging/solo6x10/solo6x10.h deleted file mode 100644 index abee7213202f..000000000000 --- a/drivers/staging/solo6x10/solo6x10.h +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 __SOLO6X10_H -#define __SOLO6X10_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "registers.h" - -#ifndef PCI_VENDOR_ID_SOFTLOGIC -#define PCI_VENDOR_ID_SOFTLOGIC 0x9413 -#define PCI_DEVICE_ID_SOLO6010 0x6010 -#define PCI_DEVICE_ID_SOLO6110 0x6110 -#endif - -#ifndef PCI_VENDOR_ID_BLUECHERRY -#define PCI_VENDOR_ID_BLUECHERRY 0x1BB3 -/* Neugent Softlogic 6010 based cards */ -#define PCI_DEVICE_ID_NEUSOLO_4 0x4304 -#define PCI_DEVICE_ID_NEUSOLO_9 0x4309 -#define PCI_DEVICE_ID_NEUSOLO_16 0x4310 -/* Bluecherry Softlogic 6010 based cards */ -#define PCI_DEVICE_ID_BC_SOLO_4 0x4E04 -#define PCI_DEVICE_ID_BC_SOLO_9 0x4E09 -#define PCI_DEVICE_ID_BC_SOLO_16 0x4E10 -/* Bluecherry Softlogic 6110 based cards */ -#define PCI_DEVICE_ID_BC_6110_4 0x5304 -#define PCI_DEVICE_ID_BC_6110_8 0x5308 -#define PCI_DEVICE_ID_BC_6110_16 0x5310 -#endif /* Bluecherry */ - -#define SOLO6X10_NAME "solo6x10" - -#define SOLO_MAX_CHANNELS 16 - -/* Make sure these two match */ -#define SOLO6X10_VERSION "2.1.0" -#define SOLO6X10_VER_MAJOR 2 -#define SOLO6X10_VER_MINOR 0 -#define SOLO6X10_VER_SUB 0 -#define SOLO6X10_VER_NUM \ - KERNEL_VERSION(SOLO6X10_VER_MAJOR, SOLO6X10_VER_MINOR, SOLO6X10_VER_SUB) - -#define FLAGS_6110 1 - -/* - * The SOLO6x10 actually has 8 i2c channels, but we only use 2. - * 0 - Techwell chip(s) - * 1 - SAA7128 - */ -#define SOLO_I2C_ADAPTERS 2 -#define SOLO_I2C_TW 0 -#define SOLO_I2C_SAA 1 - -/* DMA Engine setup */ -#define SOLO_NR_P2M 4 -#define SOLO_NR_P2M_DESC 256 -/* MPEG and JPEG share the same interrupt and locks so they must be together - * in the same dma channel. */ -#define SOLO_P2M_DMA_ID_MP4E 0 -#define SOLO_P2M_DMA_ID_JPEG 0 -#define SOLO_P2M_DMA_ID_MP4D 1 -#define SOLO_P2M_DMA_ID_G723D 1 -#define SOLO_P2M_DMA_ID_DISP 2 -#define SOLO_P2M_DMA_ID_OSG 2 -#define SOLO_P2M_DMA_ID_G723E 3 -#define SOLO_P2M_DMA_ID_VIN 3 - -/* Encoder standard modes */ -#define SOLO_ENC_MODE_CIF 2 -#define SOLO_ENC_MODE_HD1 1 -#define SOLO_ENC_MODE_D1 9 - -#define SOLO_DEFAULT_GOP 30 -#define SOLO_DEFAULT_QP 3 - -/* There is 8MB memory available for solo to buffer MPEG4 frames. - * This gives us 512 * 16kbyte queues. */ -#define SOLO_NR_RING_BUFS 512 - -#define SOLO_CLOCK_MHZ 108 - -#ifndef V4L2_BUF_FLAG_MOTION_ON -#define V4L2_BUF_FLAG_MOTION_ON 0x0400 -#define V4L2_BUF_FLAG_MOTION_DETECTED 0x0800 -#endif -#ifndef V4L2_CID_MOTION_ENABLE -#define PRIVATE_CIDS -#define V4L2_CID_MOTION_ENABLE (V4L2_CID_PRIVATE_BASE+0) -#define V4L2_CID_MOTION_THRESHOLD (V4L2_CID_PRIVATE_BASE+1) -#define V4L2_CID_MOTION_TRACE (V4L2_CID_PRIVATE_BASE+2) -#endif - -enum SOLO_I2C_STATE { - IIC_STATE_IDLE, - IIC_STATE_START, - IIC_STATE_READ, - IIC_STATE_WRITE, - IIC_STATE_STOP -}; - -struct p2m_desc { - u32 ctrl; - u32 ext; - u32 ta; - u32 fa; -}; - -struct solo_p2m_dev { - struct mutex mutex; - struct completion completion; - int error; -}; - -#define OSD_TEXT_MAX 30 - -enum solo_enc_types { - SOLO_ENC_TYPE_STD, - SOLO_ENC_TYPE_EXT, -}; - -struct solo_enc_dev { - struct solo_dev *solo_dev; - /* V4L2 Items */ - struct video_device *vfd; - /* General accounting */ - wait_queue_head_t thread_wait; - spinlock_t lock; - atomic_t readers; - u8 ch; - u8 mode, gop, qp, interlaced, interval; - u8 reset_gop; - u8 bw_weight; - u8 motion_detected; - u16 motion_thresh; - u16 width; - u16 height; - char osd_text[OSD_TEXT_MAX + 1]; -}; - -struct solo_enc_buf { - u8 vop; - u8 ch; - enum solo_enc_types type; - u32 off; - u32 size; - u32 jpeg_off; - u32 jpeg_size; - struct timeval ts; -}; - -/* The SOLO6x10 PCI Device */ -struct solo_dev { - /* General stuff */ - struct pci_dev *pdev; - u8 __iomem *reg_base; - int nr_chans; - int nr_ext; - u32 flags; - u32 irq_mask; - u32 motion_mask; - spinlock_t reg_io_lock; - - /* tw28xx accounting */ - u8 tw2865, tw2864, tw2815; - u8 tw28_cnt; - - /* i2c related items */ - struct i2c_adapter i2c_adap[SOLO_I2C_ADAPTERS]; - enum SOLO_I2C_STATE i2c_state; - struct mutex i2c_mutex; - int i2c_id; - wait_queue_head_t i2c_wait; - struct i2c_msg *i2c_msg; - unsigned int i2c_msg_num; - unsigned int i2c_msg_ptr; - - /* P2M DMA Engine */ - struct solo_p2m_dev p2m_dev[SOLO_NR_P2M]; - - /* V4L2 Display items */ - struct video_device *vfd; - unsigned int erasing; - unsigned int frame_blank; - u8 cur_disp_ch; - wait_queue_head_t disp_thread_wait; - - /* V4L2 Encoder items */ - struct solo_enc_dev *v4l2_enc[SOLO_MAX_CHANNELS]; - u16 enc_bw_remain; - /* IDX into hw mp4 encoder */ - u8 enc_idx; - /* Our software ring of enc buf references */ - u16 enc_wr_idx; - struct solo_enc_buf enc_buf[SOLO_NR_RING_BUFS]; - - /* Current video settings */ - u32 video_type; - u16 video_hsize, video_vsize; - u16 vout_hstart, vout_vstart; - u16 vin_hstart, vin_vstart; - u8 fps; - - /* Audio components */ - struct snd_card *snd_card; - struct snd_pcm *snd_pcm; - atomic_t snd_users; - int g723_hw_idx; -}; - -static inline u32 solo_reg_read(struct solo_dev *solo_dev, int reg) -{ - unsigned long flags; - u32 ret; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - ret = readl(solo_dev->reg_base + reg); - rmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); - - return ret; -} - -static inline void solo_reg_write(struct solo_dev *solo_dev, int reg, u32 data) -{ - unsigned long flags; - u16 val; - - spin_lock_irqsave(&solo_dev->reg_io_lock, flags); - - writel(data, solo_dev->reg_base + reg); - wmb(); - pci_read_config_word(solo_dev->pdev, PCI_STATUS, &val); - rmb(); - - spin_unlock_irqrestore(&solo_dev->reg_io_lock, flags); -} - -void solo_irq_on(struct solo_dev *solo_dev, u32 mask); -void solo_irq_off(struct solo_dev *solo_dev, u32 mask); - -/* Init/exit routeines for subsystems */ -int solo_disp_init(struct solo_dev *solo_dev); -void solo_disp_exit(struct solo_dev *solo_dev); - -int solo_gpio_init(struct solo_dev *solo_dev); -void solo_gpio_exit(struct solo_dev *solo_dev); - -int solo_i2c_init(struct solo_dev *solo_dev); -void solo_i2c_exit(struct solo_dev *solo_dev); - -int solo_p2m_init(struct solo_dev *solo_dev); -void solo_p2m_exit(struct solo_dev *solo_dev); - -int solo_v4l2_init(struct solo_dev *solo_dev); -void solo_v4l2_exit(struct solo_dev *solo_dev); - -int solo_enc_init(struct solo_dev *solo_dev); -void solo_enc_exit(struct solo_dev *solo_dev); - -int solo_enc_v4l2_init(struct solo_dev *solo_dev); -void solo_enc_v4l2_exit(struct solo_dev *solo_dev); - -int solo_g723_init(struct solo_dev *solo_dev); -void solo_g723_exit(struct solo_dev *solo_dev); - -/* ISR's */ -int solo_i2c_isr(struct solo_dev *solo_dev); -void solo_p2m_isr(struct solo_dev *solo_dev, int id); -void solo_p2m_error_isr(struct solo_dev *solo_dev, u32 status); -void solo_enc_v4l2_isr(struct solo_dev *solo_dev); -void solo_g723_isr(struct solo_dev *solo_dev); -void solo_motion_isr(struct solo_dev *solo_dev); -void solo_video_in_isr(struct solo_dev *solo_dev); - -/* i2c read/write */ -u8 solo_i2c_readbyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off); -void solo_i2c_writebyte(struct solo_dev *solo_dev, int id, u8 addr, u8 off, - u8 data); - -/* P2M DMA */ -int solo_p2m_dma_t(struct solo_dev *solo_dev, u8 id, int wr, - dma_addr_t dma_addr, u32 ext_addr, u32 size); -int solo_p2m_dma(struct solo_dev *solo_dev, u8 id, int wr, - void *sys_addr, u32 ext_addr, u32 size); -int solo_p2m_dma_sg(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *pdesc, int wr, - struct scatterlist *sglist, u32 sg_off, - u32 ext_addr, u32 size); -void solo_p2m_push_desc(struct p2m_desc *desc, int wr, dma_addr_t dma_addr, - u32 ext_addr, u32 size, int repeat, u32 ext_size); -int solo_p2m_dma_desc(struct solo_dev *solo_dev, u8 id, - struct p2m_desc *desc, int desc_count); - -/* Set the threshold for motion detection */ -void solo_set_motion_threshold(struct solo_dev *solo_dev, u8 ch, u16 val); -#define SOLO_DEF_MOT_THRESH 0x0300 - -/* Write text on OSD */ -int solo_osd_print(struct solo_enc_dev *solo_enc); - -#endif /* __SOLO6X10_H */ diff --git a/drivers/staging/solo6x10/tw28.c b/drivers/staging/solo6x10/tw28.c deleted file mode 100644 index db56b42c56c6..000000000000 --- a/drivers/staging/solo6x10/tw28.c +++ /dev/null @@ -1,821 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" -#include "tw28.h" - -/* XXX: Some of these values are masked into an 8-bit regs, and shifted - * around for other 8-bit regs. What are the magic bits in these values? */ -#define DEFAULT_HDELAY_NTSC (32 - 4) -#define DEFAULT_HACTIVE_NTSC (720 + 16) -#define DEFAULT_VDELAY_NTSC (7 - 2) -#define DEFAULT_VACTIVE_NTSC (240 + 4) - -#define DEFAULT_HDELAY_PAL (32 + 4) -#define DEFAULT_HACTIVE_PAL (864-DEFAULT_HDELAY_PAL) -#define DEFAULT_VDELAY_PAL (6) -#define DEFAULT_VACTIVE_PAL (312-DEFAULT_VDELAY_PAL) - -static u8 tbl_tw2864_template[] = { - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x80, 0x10, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xf5, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x40 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x70 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA3, 0x00, - 0x00, 0x02, 0x00, 0xcc, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xd8, 0xbc, 0xb8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x72, 0x3e, 0x14, 0xa5, 0xe4, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xa0, 0x88, 0x5a, 0x01, - 0x08, 0x08, 0x08, 0x08, 0x1a, 0x1a, 0x1a, 0x1a, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xf0, 0xf0, 0xf0, 0xf0, 0x44, - 0x44, 0x0a, 0x00, 0xff, 0xef, 0xef, 0xef, 0xef, /* 0xb0 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xb1, 0xe4, 0x40, 0x00, - 0x77, 0x77, 0x01, 0x13, 0x57, 0x9b, 0xdf, 0x20, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xe0, 0xbb, 0xbb, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xb5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x11, 0x40, 0xaf, 0xff, 0x00, 0x00, 0x00, -}; - -static u8 tbl_tw2865_ntsc_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x00 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x10 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x02, /* 0x20 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0xf0, 0x70, 0x48, 0x80, 0x80, 0x00, 0x02, /* 0x30 */ - 0x12, 0xff, 0x09, 0xd0, 0x00, 0x00, 0x00, 0x7f, - 0x00, 0x00, 0x90, 0x68, 0x00, 0x38, 0x80, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xE9, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1B, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xEB, 0xFF, 0xD6, 0xD8, - 0xD8, 0xD7, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x78, 0x85, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -static u8 tbl_tw2865_pal_template[] = { - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x00 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x10 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x20 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0xf0, 0x70, 0x30, 0x80, 0x80, 0x00, 0x12, /* 0x30 */ - 0x11, 0xff, 0x01, 0xc3, 0x00, 0x00, 0x01, 0x7f, - 0x00, 0x94, 0x90, 0x48, 0x00, 0x38, 0x7F, 0x80, /* 0x40 */ - 0x80, 0x80, 0x77, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x50 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x45, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x60 */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x43, - 0x08, 0x00, 0x00, 0x01, 0xf1, 0x03, 0xEF, 0x03, /* 0x70 */ - 0xEA, 0x03, 0xD9, 0x15, 0x15, 0xE4, 0xA3, 0x80, - 0x00, 0x02, 0x00, 0xCC, 0x00, 0x80, 0x44, 0x50, /* 0x80 */ - 0x22, 0x01, 0xD8, 0xBC, 0xB8, 0x44, 0x38, 0x00, - 0x00, 0x78, 0x44, 0x3D, 0x14, 0xA5, 0xE0, 0x05, /* 0x90 */ - 0x00, 0x28, 0x44, 0x44, 0xA0, 0x90, 0x52, 0x13, - 0x08, 0x08, 0x08, 0x08, 0x1A, 0x1A, 0x1A, 0x1A, /* 0xa0 */ - 0x00, 0x00, 0x00, 0xF0, 0xF0, 0xF0, 0xF0, 0x44, - 0x44, 0x4A, 0x00, 0xFF, 0xEF, 0xEF, 0xEF, 0xEF, /* 0xb0 */ - 0xFF, 0xE7, 0xE9, 0xE9, 0xE9, 0xFF, 0xD7, 0xD8, - 0xD9, 0xD8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc0 */ - 0x00, 0x00, 0x55, 0x00, 0xE4, 0x39, 0x00, 0x80, - 0x77, 0x77, 0x03, 0x20, 0x57, 0x9b, 0xdf, 0x31, /* 0xd0 */ - 0x64, 0xa8, 0xec, 0xd1, 0x0f, 0x11, 0x11, 0x81, - 0x10, 0xC0, 0xAA, 0xAA, 0x00, 0x11, 0x00, 0x00, /* 0xe0 */ - 0x11, 0x00, 0x00, 0x11, 0x00, 0x00, 0x11, 0x00, - 0x83, 0xB5, 0x09, 0x00, 0xA0, 0x00, 0x01, 0x20, /* 0xf0 */ - 0x64, 0x51, 0x40, 0xaf, 0xFF, 0xF0, 0x00, 0xC0, -}; - -#define is_tw286x(__solo, __id) (!(__solo->tw2815 & (1 << __id))) - -static u8 tw_readbyte(struct solo_dev *solo_dev, int chip_id, u8 tw6x_off, - u8 tw_off) -{ - if (is_tw286x(solo_dev, chip_id)) - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off); - else - return solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off); -} - -static void tw_writebyte(struct solo_dev *solo_dev, int chip_id, - u8 tw6x_off, u8 tw_off, u8 val) -{ - if (is_tw286x(solo_dev, chip_id)) - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw6x_off, val); - else - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_id), - tw_off, val); -} - -static void tw_write_and_verify(struct solo_dev *solo_dev, u8 addr, u8 off, - u8 val) -{ - int i; - - for (i = 0; i < 5; i++) { - u8 rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, addr, off); - if (rval == val) - return; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, addr, off, val); - msleep_interruptible(1); - } - -/* printk("solo6x10/tw28: Error writing register: %02x->%02x [%02x]\n", - addr, off, val); */ -} - -static int tw2865_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2865_common[256]; - int i; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) - memcpy(tbl_tw2865_common, tbl_tw2865_pal_template, - sizeof(tbl_tw2865_common)); - else - memcpy(tbl_tw2865_common, tbl_tw2865_ntsc_template, - sizeof(tbl_tw2865_common)); - - /* ALINK Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2865_common[0xd2] = 0x01; - tbl_tw2865_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2865_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2865_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2865_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2865_common[0xcf] = 0x80; - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - if (i >= 0xb8 && i <= 0xc1) - continue; - if ((i & ~0x30) == 0x00 || - (i & ~0x30) == 0x0c || - (i & ~0x30) == 0x0d) - continue; - if (i >= 0xc4 && i <= 0xc7) - continue; - if (i == 0xfd) - continue; - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2865_common[i]); - } - - return 0; -} - -static int tw2864_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_tw2864_common[sizeof(tbl_tw2864_template)]; - int i; - - memcpy(tbl_tw2864_common, tbl_tw2864_template, - sizeof(tbl_tw2864_common)); - - if (solo_dev->tw2865 == 0) { - /* IRQ Mode */ - if (solo_dev->nr_chans == 4) { - tbl_tw2864_common[0xd2] = 0x01; - tbl_tw2864_common[0xcf] = 0x00; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x40; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x43; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x40; - } - } else { - /* ALINK Mode. Assumes that the first tw28xx is a - * 2865 and these are in cascade. */ - for (i = 0; i <= 4; i++) - tbl_tw2864_common[0x08 | i << 4] = 0x12; - - if (solo_dev->nr_chans == 8) { - tbl_tw2864_common[0xd2] = 0x02; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x80; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2864_common[0xd2] = 0x03; - if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2864_common[0xcf] = 0x83; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2864_common[0xcf] = 0x80; - } - } - - /* NTSC or PAL */ - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_PAL) { - for (i = 0; i < 4; i++) { - tbl_tw2864_common[0x07 | (i << 4)] |= 0x10; - tbl_tw2864_common[0x08 | (i << 4)] |= 0x06; - tbl_tw2864_common[0x0a | (i << 4)] |= 0x08; - tbl_tw2864_common[0x0b | (i << 4)] |= 0x13; - tbl_tw2864_common[0x0e | (i << 4)] |= 0x01; - } - tbl_tw2864_common[0x9d] = 0x90; - tbl_tw2864_common[0xf3] = 0x00; - tbl_tw2864_common[0xf4] = 0xa0; - } - - for (i = 0; i < 0xff; i++) { - /* Skip read only registers */ - if (i >= 0xb8 && i <= 0xc1) - continue; - if ((i & ~0x30) == 0x00 || - (i & ~0x30) == 0x0c || - (i & ~0x30) == 0x0d) - continue; - if (i == 0x74 || i == 0x77 || i == 0x78 || - i == 0x79 || i == 0x7a) - continue; - if (i == 0xfd) - continue; - - tw_write_and_verify(solo_dev, dev_addr, i, - tbl_tw2864_common[i]); - } - - return 0; -} - -static int tw2815_setup(struct solo_dev *solo_dev, u8 dev_addr) -{ - u8 tbl_ntsc_tw2815_common[] = { - 0x00, 0xc8, 0x20, 0xd0, 0x06, 0xf0, 0x08, 0x80, - 0x80, 0x80, 0x80, 0x02, 0x06, 0x00, 0x11, - }; - - u8 tbl_pal_tw2815_common[] = { - 0x00, 0x88, 0x20, 0xd0, 0x05, 0x20, 0x28, 0x80, - 0x80, 0x80, 0x80, 0x82, 0x06, 0x00, 0x11, - }; - - u8 tbl_tw2815_sfr[] = { - 0x00, 0x00, 0x00, 0xc0, 0x45, 0xa0, 0xd0, 0x2f, /* 0x00 */ - 0x64, 0x80, 0x80, 0x82, 0x82, 0x00, 0x00, 0x00, - 0x00, 0x0f, 0x05, 0x00, 0x00, 0x80, 0x06, 0x00, /* 0x10 */ - 0x00, 0x00, 0x00, 0xff, 0x8f, 0x00, 0x00, 0x00, - 0x88, 0x88, 0xc0, 0x00, 0x20, 0x64, 0xa8, 0xec, /* 0x20 */ - 0x31, 0x75, 0xb9, 0xfd, 0x00, 0x00, 0x88, 0x88, - 0x88, 0x11, 0x00, 0x88, 0x88, 0x00, /* 0x30 */ - }; - u8 *tbl_tw2815_common; - int i; - int ch; - - tbl_ntsc_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_ntsc_tw2815_common[0x02] = DEFAULT_HDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_NTSC >> 8); - - /* Horizontal Active Control */ - tbl_ntsc_tw2815_common[0x03] = DEFAULT_HACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_NTSC >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_ntsc_tw2815_common[0x04] = DEFAULT_VDELAY_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_NTSC >> 8)) << 4); - - /* Vertical Active Control */ - tbl_ntsc_tw2815_common[0x05] = DEFAULT_VACTIVE_NTSC & 0xff; - tbl_ntsc_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_NTSC >> 8)) << 5); - - tbl_pal_tw2815_common[0x06] = 0; - - /* Horizontal Delay Control */ - tbl_pal_tw2815_common[0x02] = DEFAULT_HDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= 0x03 & (DEFAULT_HDELAY_PAL >> 8); - - /* Horizontal Active Control */ - tbl_pal_tw2815_common[0x03] = DEFAULT_HACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x03 & (DEFAULT_HACTIVE_PAL >> 8)) << 2); - - /* Vertical Delay Control */ - tbl_pal_tw2815_common[0x04] = DEFAULT_VDELAY_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VDELAY_PAL >> 8)) << 4); - - /* Vertical Active Control */ - tbl_pal_tw2815_common[0x05] = DEFAULT_VACTIVE_PAL & 0xff; - tbl_pal_tw2815_common[0x06] |= - ((0x01 & (DEFAULT_VACTIVE_PAL >> 8)) << 5); - - tbl_tw2815_common = - (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) ? - tbl_ntsc_tw2815_common : tbl_pal_tw2815_common; - - /* Dual ITU-R BT.656 format */ - tbl_tw2815_common[0x0d] |= 0x04; - - /* Audio configuration */ - tbl_tw2815_sfr[0x62 - 0x40] &= ~(3 << 6); - - if (solo_dev->nr_chans == 4) { - tbl_tw2815_sfr[0x63 - 0x40] |= 1; - tbl_tw2815_sfr[0x62 - 0x40] |= 3 << 6; - } else if (solo_dev->nr_chans == 8) { - tbl_tw2815_sfr[0x63 - 0x40] |= 2; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } else if (solo_dev->nr_chans == 16) { - tbl_tw2815_sfr[0x63 - 0x40] |= 3; - if (dev_addr == TW_CHIP_OFFSET_ADDR(0)) - tbl_tw2815_sfr[0x62 - 0x40] |= 1 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(1)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(2)) - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 6; - else if (dev_addr == TW_CHIP_OFFSET_ADDR(3)) - tbl_tw2815_sfr[0x62 - 0x40] |= 2 << 6; - } - - /* Output mode of R_ADATM pin (0 mixing, 1 record) */ - /* tbl_tw2815_sfr[0x63 - 0x40] |= 0 << 2; */ - - /* 8KHz, used to be 16KHz, but changed for remote client compat */ - tbl_tw2815_sfr[0x62 - 0x40] |= 0 << 2; - tbl_tw2815_sfr[0x6c - 0x40] |= 0 << 2; - - /* Playback of right channel */ - tbl_tw2815_sfr[0x6c - 0x40] |= 1 << 5; - - /* Reserved value (XXX ??) */ - tbl_tw2815_sfr[0x5c - 0x40] |= 1 << 5; - - /* Analog output gain and mix ratio playback on full */ - tbl_tw2815_sfr[0x70 - 0x40] |= 0xff; - /* Select playback audio and mute all except */ - tbl_tw2815_sfr[0x71 - 0x40] |= 0x10; - tbl_tw2815_sfr[0x6d - 0x40] |= 0x0f; - - /* End of audio configuration */ - - for (ch = 0; ch < 4; ch++) { - tbl_tw2815_common[0x0d] &= ~3; - switch (ch) { - case 0: - tbl_tw2815_common[0x0d] |= 0x21; - break; - case 1: - tbl_tw2815_common[0x0d] |= 0x20; - break; - case 2: - tbl_tw2815_common[0x0d] |= 0x23; - break; - case 3: - tbl_tw2815_common[0x0d] |= 0x22; - break; - } - - for (i = 0; i < 0x0f; i++) { - if (i == 0x00) - continue; /* read-only */ - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - dev_addr, (ch * 0x10) + i, - tbl_tw2815_common[i]); - } - } - - for (i = 0x40; i < 0x76; i++) { - /* Skip read-only and nop registers */ - if (i == 0x40 || i == 0x59 || i == 0x5a || - i == 0x5d || i == 0x5e || i == 0x5f) - continue; - - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, dev_addr, i, - tbl_tw2815_sfr[i - 0x40]); - } - - return 0; -} - -#define FIRST_ACTIVE_LINE 0x0008 -#define LAST_ACTIVE_LINE 0x0102 - -static void saa7128_setup(struct solo_dev *solo_dev) -{ - int i; - unsigned char regs[128] = { - 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1C, 0x2B, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, - 0x59, 0x1d, 0x75, 0x3f, 0x06, 0x3f, 0x00, 0x00, - 0x1c, 0x33, 0x00, 0x3f, 0x00, 0x00, 0x3f, 0x00, - 0x1a, 0x1a, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x68, 0x10, 0x97, 0x4c, 0x18, - 0x9b, 0x93, 0x9f, 0xff, 0x7c, 0x34, 0x3f, 0x3f, - 0x3f, 0x83, 0x83, 0x80, 0x0d, 0x0f, 0xc3, 0x06, - 0x02, 0x80, 0x71, 0x77, 0xa7, 0x67, 0x66, 0x2e, - 0x7b, 0x11, 0x4f, 0x1f, 0x7c, 0xf0, 0x21, 0x77, - 0x41, 0x88, 0x41, 0x12, 0xed, 0x10, 0x10, 0x00, - 0x41, 0xc3, 0x00, 0x3e, 0xb8, 0x02, 0x00, 0x00, - 0x00, 0x00, 0x08, 0xff, 0x80, 0x00, 0xff, 0xff, - }; - - regs[0x7A] = FIRST_ACTIVE_LINE & 0xff; - regs[0x7B] = LAST_ACTIVE_LINE & 0xff; - regs[0x7C] = ((1 << 7) | - (((LAST_ACTIVE_LINE >> 8) & 1) << 6) | - (((FIRST_ACTIVE_LINE >> 8) & 1) << 4)); - - /* PAL: XXX: We could do a second set of regs to avoid this */ - if (solo_dev->video_type != SOLO_VO_FMT_TYPE_NTSC) { - regs[0x28] = 0xE1; - - regs[0x5A] = 0x0F; - regs[0x61] = 0x02; - regs[0x62] = 0x35; - regs[0x63] = 0xCB; - regs[0x64] = 0x8A; - regs[0x65] = 0x09; - regs[0x66] = 0x2A; - - regs[0x6C] = 0xf1; - regs[0x6E] = 0x20; - - regs[0x7A] = 0x06 + 12; - regs[0x7b] = 0x24 + 12; - regs[0x7c] |= 1 << 6; - } - - /* First 0x25 bytes are read-only? */ - for (i = 0x26; i < 128; i++) { - if (i == 0x60 || i == 0x7D) - continue; - solo_i2c_writebyte(solo_dev, SOLO_I2C_SAA, 0x46, i, regs[i]); - } - - return; -} - -int solo_tw28_init(struct solo_dev *solo_dev) -{ - int i; - u8 value; - - /* Detect techwell chip type */ - for (i = 0; i < TW_NUM_CHIP; i++) { - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), 0xFF); - - switch (value >> 3) { - case 0x18: - solo_dev->tw2865 |= 1 << i; - solo_dev->tw28_cnt++; - break; - case 0x0c: - solo_dev->tw2864 |= 1 << i; - solo_dev->tw28_cnt++; - break; - default: - value = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(i), 0x59); - if ((value >> 3) == 0x04) { - solo_dev->tw2815 |= 1 << i; - solo_dev->tw28_cnt++; - } - } - } - - if (!solo_dev->tw28_cnt) - return -EINVAL; - - saa7128_setup(solo_dev); - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - if ((solo_dev->tw2865 & (1 << i))) - tw2865_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else if ((solo_dev->tw2864 & (1 << i))) - tw2864_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - else - tw2815_setup(solo_dev, TW_CHIP_OFFSET_ADDR(i)); - } - - dev_info(&solo_dev->pdev->dev, "Initialized %d tw28xx chip%s:", - solo_dev->tw28_cnt, solo_dev->tw28_cnt == 1 ? "" : "s"); - - if (solo_dev->tw2865) - printk(" tw2865[%d]", hweight32(solo_dev->tw2865)); - if (solo_dev->tw2864) - printk(" tw2864[%d]", hweight32(solo_dev->tw2864)); - if (solo_dev->tw2815) - printk(" tw2815[%d]", hweight32(solo_dev->tw2815)); - printk("\n"); - - return 0; -} - -/* - * We accessed the video status signal in the Techwell chip through - * iic/i2c because the video status reported by register REG_VI_STATUS1 - * (address 0x012C) of the SOLO6010 chip doesn't give the correct video - * status signal values. - */ -int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch) -{ - u8 val, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, TW286X_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0x0f; - - return val & (1 << ch) ? 1 : 0; -} - -#if 0 -/* Status of audio from up to 4 techwell chips are combined into 1 variable. - * See techwell datasheet for details. */ -u16 tw28_get_audio_status(struct solo_dev *solo_dev) -{ - u8 val; - u16 status = 0; - int i; - - for (i = 0; i < solo_dev->tw28_cnt; i++) { - val = (tw_readbyte(solo_dev, i, TW286X_AV_STAT_ADDR, - TW_AV_STAT_ADDR) & 0xf0) >> 4; - status |= val << (i * 4); - } - - return status; -} -#endif - -int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val) -{ - char sval; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - if (val > 255 || val < 0) - return -ERANGE; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (val > 0x0f || val < 0) - return -ERANGE; - if (is_tw286x(solo_dev, chip_num)) { - u8 v = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - v &= 0xf0; - v |= val; - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num), v); - } else if (val != 0) - return -ERANGE; - break; - - case V4L2_CID_HUE: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch), sval); - - break; - - case V4L2_CID_SATURATION: - if (is_tw286x(solo_dev, chip_num)) { - solo_i2c_writebyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SATURATIONU_ADDR(ch), val); - } - tw_writebyte(solo_dev, chip_num, TW286x_SATURATIONV_ADDR(ch), - TW_SATURATION_ADDR(ch), val); - - break; - - case V4L2_CID_CONTRAST: - tw_writebyte(solo_dev, chip_num, TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch), val); - break; - - case V4L2_CID_BRIGHTNESS: - if (is_tw286x(solo_dev, chip_num)) - sval = val - 128; - else - sval = (char)val; - tw_writebyte(solo_dev, chip_num, TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch), sval); - - break; - default: - return -EINVAL; - } - - return 0; -} - -int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, - s32 *val) -{ - u8 rval, chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - switch (ctrl) { - case V4L2_CID_SHARPNESS: - /* Only 286x has sharpness */ - if (is_tw286x(solo_dev, chip_num)) { - rval = solo_i2c_readbyte(solo_dev, SOLO_I2C_TW, - TW_CHIP_OFFSET_ADDR(chip_num), - TW286x_SHARPNESS(chip_num)); - *val = rval & 0x0f; - } else - *val = 0; - break; - case V4L2_CID_HUE: - rval = tw_readbyte(solo_dev, chip_num, TW286x_HUE_ADDR(ch), - TW_HUE_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - case V4L2_CID_SATURATION: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_SATURATIONU_ADDR(ch), - TW_SATURATION_ADDR(ch)); - break; - case V4L2_CID_CONTRAST: - *val = tw_readbyte(solo_dev, chip_num, - TW286x_CONTRAST_ADDR(ch), - TW_CONTRAST_ADDR(ch)); - break; - case V4L2_CID_BRIGHTNESS: - rval = tw_readbyte(solo_dev, chip_num, - TW286x_BRIGHTNESS_ADDR(ch), - TW_BRIGHTNESS_ADDR(ch)); - if (is_tw286x(solo_dev, chip_num)) - *val = (s32)((char)rval) + 128; - else - *val = rval; - break; - default: - return -EINVAL; - } - - return 0; -} - -#if 0 -/* - * For audio output volume, the output channel is only 1. In this case we - * don't need to offset TW_CHIP_OFFSET_ADDR. The TW_CHIP_OFFSET_ADDR used - * is the base address of the techwell chip. - */ -void tw2815_Set_AudioOutVol(struct solo_dev *solo_dev, unsigned int u_val) -{ - unsigned int val; - unsigned int chip_num; - - chip_num = (solo_dev->nr_chans - 1) / 4; - - val = tw_readbyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR); - - u_val = (val & 0x0f) | (u_val << 4); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_OUTPUT_VOL_ADDR, - TW_AUDIO_OUTPUT_VOL_ADDR, u_val); -} -#endif - -u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch) -{ - u8 val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - return (ch % 2) ? (val >> 4) : (val & 0x0f); -} - -void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val) -{ - u8 old_val; - u8 chip_num; - - /* Get the right chip and on-chip channel */ - chip_num = ch / 4; - ch %= 4; - - old_val = tw_readbyte(solo_dev, chip_num, - TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch)); - - val = (old_val & ((ch % 2) ? 0x0f : 0xf0)) | - ((ch % 2) ? (val << 4) : val); - - tw_writebyte(solo_dev, chip_num, TW286x_AUDIO_INPUT_GAIN_ADDR(ch), - TW_AUDIO_INPUT_GAIN_ADDR(ch), val); -} diff --git a/drivers/staging/solo6x10/tw28.h b/drivers/staging/solo6x10/tw28.h deleted file mode 100644 index a44a03afbd30..000000000000 --- a/drivers/staging/solo6x10/tw28.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 __SOLO6X10_TW28_H -#define __SOLO6X10_TW28_H - -#include "solo6x10.h" - -#define TW_NUM_CHIP 4 -#define TW_BASE_ADDR 0x28 -#define TW_CHIP_OFFSET_ADDR(n) (TW_BASE_ADDR + (n)) - -/* tw2815 */ -#define TW_AV_STAT_ADDR 0x5a -#define TW_HUE_ADDR(n) (0x07 | ((n) << 4)) -#define TW_SATURATION_ADDR(n) (0x08 | ((n) << 4)) -#define TW_CONTRAST_ADDR(n) (0x09 | ((n) << 4)) -#define TW_BRIGHTNESS_ADDR(n) (0x0a | ((n) << 4)) -#define TW_AUDIO_OUTPUT_VOL_ADDR 0x70 -#define TW_AUDIO_INPUT_GAIN_ADDR(n) (0x60 + ((n > 1) ? 1 : 0)) - -/* tw286x */ -#define TW286X_AV_STAT_ADDR 0xfd -#define TW286x_HUE_ADDR(n) (0x06 | ((n) << 4)) -#define TW286x_SATURATIONU_ADDR(n) (0x04 | ((n) << 4)) -#define TW286x_SATURATIONV_ADDR(n) (0x05 | ((n) << 4)) -#define TW286x_CONTRAST_ADDR(n) (0x02 | ((n) << 4)) -#define TW286x_BRIGHTNESS_ADDR(n) (0x01 | ((n) << 4)) -#define TW286x_SHARPNESS(n) (0x03 | ((n) << 4)) -#define TW286x_AUDIO_OUTPUT_VOL_ADDR 0xdf -#define TW286x_AUDIO_INPUT_GAIN_ADDR(n) (0xD0 + ((n > 1) ? 1 : 0)) - -int solo_tw28_init(struct solo_dev *solo_dev); - -int tw28_set_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 val); -int tw28_get_ctrl_val(struct solo_dev *solo_dev, u32 ctrl, u8 ch, s32 *val); - -u8 tw28_get_audio_gain(struct solo_dev *solo_dev, u8 ch); -void tw28_set_audio_gain(struct solo_dev *solo_dev, u8 ch, u8 val); -int tw28_get_video_status(struct solo_dev *solo_dev, u8 ch); - -#if 0 -unsigned int tw2815_get_audio_status(struct SOLO *solo); -void tw2815_Set_AudioOutVol(struct SOLO *solo, unsigned int u_val); -#endif - -#endif /* __SOLO6X10_TW28_H */ diff --git a/drivers/staging/solo6x10/v4l2-enc.c b/drivers/staging/solo6x10/v4l2-enc.c deleted file mode 100644 index bee7280bbed9..000000000000 --- a/drivers/staging/solo6x10/v4l2-enc.c +++ /dev/null @@ -1,1825 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" -#include "tw28.h" -#include "jpeg.h" - -#define MIN_VID_BUFFERS 4 -#define FRAME_BUF_SIZE (128 * 1024) -#define MP4_QS 16 - -static int solo_enc_thread(void *data); - -extern unsigned video_nr; - -struct solo_enc_fh { - struct solo_enc_dev *enc; - u32 fmt; - u16 rd_idx; - u8 enc_on; - enum solo_enc_types type; - struct videobuf_queue vidq; - struct list_head vidq_active; - struct task_struct *kthread; - struct p2m_desc desc[SOLO_NR_P2M_DESC]; -}; - -static const u32 solo_user_ctrls[] = { - V4L2_CID_BRIGHTNESS, - V4L2_CID_CONTRAST, - V4L2_CID_SATURATION, - V4L2_CID_HUE, - V4L2_CID_SHARPNESS, - 0 -}; - -static const u32 solo_mpeg_ctrls[] = { - V4L2_CID_MPEG_VIDEO_ENCODING, - V4L2_CID_MPEG_VIDEO_GOP_SIZE, - 0 -}; - -static const u32 solo_private_ctrls[] = { - V4L2_CID_MOTION_ENABLE, - V4L2_CID_MOTION_THRESHOLD, - 0 -}; - -static const u32 solo_fmtx_ctrls[] = { - V4L2_CID_RDS_TX_RADIO_TEXT, - 0 -}; - -static const u32 *solo_ctrl_classes[] = { - solo_user_ctrls, - solo_mpeg_ctrls, - solo_fmtx_ctrls, - solo_private_ctrls, - NULL -}; - -static int solo_is_motion_on(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 ch = solo_enc->ch; - - if (solo_dev->motion_mask & (1 << ch)) - return 1; - return 0; -} - -static void solo_motion_toggle(struct solo_enc_dev *solo_enc, int on) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 ch = solo_enc->ch; - - spin_lock(&solo_enc->lock); - - if (on) - solo_dev->motion_mask |= (1 << ch); - else - solo_dev->motion_mask &= ~(1 << ch); - - /* Do this regardless of if we are turning on or off */ - solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, - 1 << solo_enc->ch); - solo_enc->motion_detected = 0; - - solo_reg_write(solo_dev, SOLO_VI_MOT_ADR, - SOLO_VI_MOTION_EN(solo_dev->motion_mask) | - (SOLO_MOTION_EXT_ADDR(solo_dev) >> 16)); - - if (solo_dev->motion_mask) - solo_irq_on(solo_dev, SOLO_IRQ_MOTION); - else - solo_irq_off(solo_dev, SOLO_IRQ_MOTION); - - spin_unlock(&solo_enc->lock); -} - -/* Should be called with solo_enc->lock held */ -static void solo_update_mode(struct solo_enc_dev *solo_enc) -{ - struct solo_dev *solo_dev = solo_enc->solo_dev; - - assert_spin_locked(&solo_enc->lock); - - solo_enc->interlaced = (solo_enc->mode & 0x08) ? 1 : 0; - solo_enc->bw_weight = max(solo_dev->fps / solo_enc->interval, 1); - - switch (solo_enc->mode) { - case SOLO_ENC_MODE_CIF: - solo_enc->width = solo_dev->video_hsize >> 1; - solo_enc->height = solo_dev->video_vsize; - break; - case SOLO_ENC_MODE_D1: - solo_enc->width = solo_dev->video_hsize; - solo_enc->height = solo_dev->video_vsize << 1; - solo_enc->bw_weight <<= 2; - break; - default: - WARN(1, "mode is unknown\n"); - } -} - -/* Should be called with solo_enc->lock held */ -static int solo_enc_on(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - u8 ch = solo_enc->ch; - struct solo_dev *solo_dev = solo_enc->solo_dev; - u8 interval; - - assert_spin_locked(&solo_enc->lock); - - if (fh->enc_on) - return 0; - - solo_update_mode(solo_enc); - - /* Make sure to bw check on first reader */ - if (!atomic_read(&solo_enc->readers)) { - if (solo_enc->bw_weight > solo_dev->enc_bw_remain) - return -EBUSY; - else - solo_dev->enc_bw_remain -= solo_enc->bw_weight; - } - - fh->enc_on = 1; - fh->rd_idx = solo_enc->solo_dev->enc_wr_idx; - - if (fh->type == SOLO_ENC_TYPE_EXT) - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(ch), 1); - - if (atomic_inc_return(&solo_enc->readers) > 1) - return 0; - - /* Disable all encoding for this channel */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), 0); - - /* Common for both std and ext encoding */ - solo_reg_write(solo_dev, SOLO_VE_CH_INTL(ch), - solo_enc->interlaced ? 1 : 0); - - if (solo_enc->interlaced) - interval = solo_enc->interval - 1; - else - interval = solo_enc->interval; - - /* Standard encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV(ch), interval); - - /* Extended encoding only */ - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(ch), solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_QP_E(ch), solo_enc->qp); - solo_reg_write(solo_dev, SOLO_CAP_CH_INTV_E(ch), interval); - - /* Enables the standard encoder */ - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(ch), solo_enc->mode); - - /* Settle down Beavis... */ - mdelay(10); - - return 0; -} - -static void solo_enc_off(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (!fh->enc_on) - return; - - if (fh->kthread) { - kthread_stop(fh->kthread); - fh->kthread = NULL; - } - - solo_dev->enc_bw_remain += solo_enc->bw_weight; - fh->enc_on = 0; - - if (atomic_dec_return(&solo_enc->readers) > 0) - return; - - solo_reg_write(solo_dev, SOLO_CAP_CH_SCALE(solo_enc->ch), 0); - solo_reg_write(solo_dev, SOLO_CAP_CH_COMP_ENA_E(solo_enc->ch), 0); -} - -static int solo_start_fh_thread(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - - fh->kthread = kthread_run(solo_enc_thread, fh, SOLO6X10_NAME "_enc"); - - /* Oops, we had a problem */ - if (IS_ERR(fh->kthread)) { - spin_lock(&solo_enc->lock); - solo_enc_off(fh); - spin_unlock(&solo_enc->lock); - - return PTR_ERR(fh->kthread); - } - - return 0; -} - -static void enc_reset_gop(struct solo_dev *solo_dev, u8 ch) -{ - BUG_ON(ch >= solo_dev->nr_chans); - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), 1); - solo_dev->v4l2_enc[ch]->reset_gop = 1; -} - -static int enc_gop_reset(struct solo_dev *solo_dev, u8 ch, u8 vop) -{ - BUG_ON(ch >= solo_dev->nr_chans); - if (!solo_dev->v4l2_enc[ch]->reset_gop) - return 0; - if (vop) - return 1; - solo_dev->v4l2_enc[ch]->reset_gop = 0; - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(ch), - solo_dev->v4l2_enc[ch]->gop); - return 0; -} - -static void enc_write_sg(struct scatterlist *sglist, void *buf, int size) -{ - struct scatterlist *sg; - u8 *src = buf; - - for (sg = sglist; sg && size > 0; sg = sg_next(sg)) { - u8 *p = sg_virt(sg); - size_t len = sg_dma_len(sg); - int i; - - for (i = 0; i < len && size; i++) - p[i] = *(src++); - } -} - -static int enc_get_mpeg_dma_sg(struct solo_dev *solo_dev, - struct p2m_desc *desc, - struct scatterlist *sglist, int skip, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, - desc, 0, sglist, skip, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, - sglist, skip, SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_MP4E, desc, 0, - sglist, skip + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); - - return ret; -} - -static int enc_get_mpeg_dma_t(struct solo_dev *solo_dev, - dma_addr_t buf, unsigned int off, - unsigned int size) -{ - int ret; - - if (off > SOLO_MP4E_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_MP4E_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, buf, - SOLO_MP4E_EXT_ADDR(solo_dev) + off, - SOLO_MP4E_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_t(solo_dev, SOLO_P2M_DMA_ID_MP4E, 0, - buf + SOLO_MP4E_EXT_SIZE(solo_dev) - off, - SOLO_MP4E_EXT_ADDR(solo_dev), - size + off - SOLO_MP4E_EXT_SIZE(solo_dev)); - - return ret; -} - -static int enc_get_mpeg_dma(struct solo_dev *solo_dev, void *buf, - unsigned int off, unsigned int size) -{ - int ret; - - dma_addr_t dma_addr = pci_map_single(solo_dev->pdev, buf, size, - PCI_DMA_FROMDEVICE); - ret = enc_get_mpeg_dma_t(solo_dev, dma_addr, off, size); - pci_unmap_single(solo_dev->pdev, dma_addr, size, PCI_DMA_FROMDEVICE); - - return ret; -} - -static int enc_get_jpeg_dma_sg(struct solo_dev *solo_dev, - struct p2m_desc *desc, - struct scatterlist *sglist, int skip, - unsigned int off, unsigned int size) -{ - int ret; - - if (off > SOLO_JPEG_EXT_SIZE(solo_dev)) - return -EINVAL; - - if (off + size <= SOLO_JPEG_EXT_SIZE(solo_dev)) { - return solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, - desc, 0, sglist, skip, - SOLO_JPEG_EXT_ADDR(solo_dev) + off, size); - } - - /* Buffer wrap */ - ret = solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, - sglist, skip, SOLO_JPEG_EXT_ADDR(solo_dev) + off, - SOLO_JPEG_EXT_SIZE(solo_dev) - off); - - ret |= solo_p2m_dma_sg(solo_dev, SOLO_P2M_DMA_ID_JPEG, desc, 0, - sglist, skip + SOLO_JPEG_EXT_SIZE(solo_dev) - off, - SOLO_JPEG_EXT_ADDR(solo_dev), - size + off - SOLO_JPEG_EXT_SIZE(solo_dev)); - - return ret; -} - -/* Returns true of __chk is within the first __range bytes of __off */ -#define OFF_IN_RANGE(__off, __range, __chk) \ - ((__off <= __chk) && ((__off + __range) >= __chk)) - -static void solo_jpeg_header(struct solo_enc_dev *solo_enc, - struct videobuf_dmabuf *vbuf) -{ - struct scatterlist *sg; - void *src = jpeg_header; - size_t copied = 0; - size_t to_copy = sizeof(jpeg_header); - - for (sg = vbuf->sglist; sg && copied < to_copy; sg = sg_next(sg)) { - size_t this_copy = min(sg_dma_len(sg), - (unsigned int)(to_copy - copied)); - u8 *p = sg_virt(sg); - - memcpy(p, src + copied, this_copy); - - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 5)) - p[(SOF0_START + 5) - copied] = - 0xff & (solo_enc->height >> 8); - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 6)) - p[(SOF0_START + 6) - copied] = 0xff & solo_enc->height; - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 7)) - p[(SOF0_START + 7) - copied] = - 0xff & (solo_enc->width >> 8); - if (OFF_IN_RANGE(copied, this_copy, SOF0_START + 8)) - p[(SOF0_START + 8) - copied] = 0xff & solo_enc->width; - - copied += this_copy; - } -} - -static int solo_fill_jpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, - struct videobuf_buffer *vb, - struct videobuf_dmabuf *vbuf) -{ - struct solo_dev *solo_dev = fh->enc->solo_dev; - int size = enc_buf->jpeg_size; - - /* Copy the header first (direct write) */ - solo_jpeg_header(fh->enc, vbuf); - - vb->size = size + sizeof(jpeg_header); - - /* Grab the jpeg frame */ - return enc_get_jpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, - sizeof(jpeg_header), - enc_buf->jpeg_off, size); -} - -static inline int vop_interlaced(__le32 *vh) -{ - return (__le32_to_cpu(vh[0]) >> 30) & 1; -} - -static inline u32 vop_size(__le32 *vh) -{ - return __le32_to_cpu(vh[0]) & 0xFFFFF; -} - -static inline u8 vop_hsize(__le32 *vh) -{ - return (__le32_to_cpu(vh[1]) >> 8) & 0xFF; -} - -static inline u8 vop_vsize(__le32 *vh) -{ - return __le32_to_cpu(vh[1]) & 0xFF; -} - -/* must be called with *bits % 8 = 0 */ -static void write_bytes(u8 **out, unsigned *bits, const u8 *src, unsigned count) -{ - memcpy(*out, src, count); - *out += count; - *bits += count * 8; -} - -static void write_bits(u8 **out, unsigned *bits, u32 value, unsigned count) -{ - - value <<= 32 - count; // shift to the right - - while (count--) { - **out <<= 1; - **out |= !!(value & (1 << 31)); /* MSB */ - value <<= 1; - if (++(*bits) % 8 == 0) - (*out)++; - } -} - -static void write_ue(u8 **out, unsigned *bits, unsigned value) /* H.264 only */ -{ - uint32_t max = 0, cnt = 0; - - while (value > max) { - max = (max + 2) * 2 - 2; - cnt++; - } - write_bits(out, bits, 1, cnt + 1); - write_bits(out, bits, ~(max - value), cnt); -} - -static void write_se(u8 **out, unsigned *bits, int value) /* H.264 only */ -{ - if (value <= 0) - write_ue(out, bits, -value * 2); - else - write_ue(out, bits, value * 2 - 1); -} - -static void write_mpeg4_end(u8 **out, unsigned *bits) -{ - write_bits(out, bits, 0, 1); - /* align on 32-bit boundary */ - if (*bits % 32) - write_bits(out, bits, 0xFFFFFFFF, 32 - *bits % 32); -} - -static void write_h264_end(u8 **out, unsigned *bits, int align) -{ - write_bits(out, bits, 1, 1); - while ((*bits) % 8) - write_bits(out, bits, 0, 1); - if (align) - while ((*bits) % 32) - write_bits(out, bits, 0, 1); -} - -static void mpeg4_write_vol(u8 **out, struct solo_dev *solo_dev, - __le32 *vh, unsigned fps, unsigned interval) -{ - static const u8 hdr[] = { - 0, 0, 1, 0x00 /* video_object_start_code */, - 0, 0, 1, 0x20 /* video_object_layer_start_code */ - }; - unsigned bits = 0; - unsigned width = vop_hsize(vh) << 4; - unsigned height = vop_vsize(vh) << 4; - unsigned interlaced = vop_interlaced(vh); - - write_bytes(out, &bits, hdr, sizeof(hdr)); - write_bits(out, &bits, 0, 1); /* random_accessible_vol */ - write_bits(out, &bits, 0x04, 8); /* video_object_type_indication: main */ - write_bits(out, &bits, 1, 1); /* is_object_layer_identifier */ - write_bits(out, &bits, 2, 4); /* video_object_layer_verid: table V2-39 */ - write_bits(out, &bits, 0, 3); /* video_object_layer_priority */ - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - write_bits(out, &bits, 3, 4); /* aspect_ratio_info, assuming 4:3 */ - else - write_bits(out, &bits, 2, 4); - write_bits(out, &bits, 1, 1); /* vol_control_parameters */ - write_bits(out, &bits, 1, 2); /* chroma_format: 4:2:0 */ - write_bits(out, &bits, 1, 1); /* low_delay */ - write_bits(out, &bits, 0, 1); /* vbv_parameters */ - write_bits(out, &bits, 0, 2); /* video_object_layer_shape: rectangular */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, fps, 16); /* vop_time_increment_resolution */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, 1, 1); /* fixed_vop_rate */ - write_bits(out, &bits, interval, 15); /* fixed_vop_time_increment */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, width, 13); /* video_object_layer_width */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, height, 13); /* video_object_layer_height */ - write_bits(out, &bits, 1, 1); /* marker_bit */ - write_bits(out, &bits, interlaced, 1); /* interlaced */ - write_bits(out, &bits, 1, 1); /* obmc_disable */ - write_bits(out, &bits, 0, 2); /* sprite_enable */ - write_bits(out, &bits, 0, 1); /* not_8_bit */ - write_bits(out, &bits, 1, 0); /* quant_type */ - write_bits(out, &bits, 0, 1); /* load_intra_quant_mat */ - write_bits(out, &bits, 0, 1); /* load_nonintra_quant_mat */ - write_bits(out, &bits, 0, 1); /* quarter_sample */ - write_bits(out, &bits, 1, 1); /* complexity_estimation_disable */ - write_bits(out, &bits, 1, 1); /* resync_marker_disable */ - write_bits(out, &bits, 0, 1); /* data_partitioned */ - write_bits(out, &bits, 0, 1); /* newpred_enable */ - write_bits(out, &bits, 0, 1); /* reduced_resolution_vop_enable */ - write_bits(out, &bits, 0, 1); /* scalability */ - write_mpeg4_end(out, &bits); -} - -static void h264_write_vol(u8 **out, struct solo_dev *solo_dev, __le32 *vh) -{ - static const u8 sps[] = { - 0, 0, 0, 1 /* start code */, 0x67, 66 /* profile_idc */, - 0 /* constraints */, 30 /* level_idc */ - }; - static const u8 pps[] = { - 0, 0, 0, 1 /* start code */, 0x68 - }; - - unsigned bits = 0; - unsigned mbs_w = vop_hsize(vh); - unsigned mbs_h = vop_vsize(vh); - - write_bytes(out, &bits, sps, sizeof(sps)); - write_ue(out, &bits, 0); /* seq_parameter_set_id */ - write_ue(out, &bits, 5); /* log2_max_frame_num_minus4 */ - write_ue(out, &bits, 0); /* pic_order_cnt_type */ - write_ue(out, &bits, 6); /* log2_max_pic_order_cnt_lsb_minus4 */ - write_ue(out, &bits, 1); /* max_num_ref_frames */ - write_bits(out, &bits, 0, 1); /* gaps_in_frame_num_value_allowed_flag */ - write_ue(out, &bits, mbs_w - 1); /* pic_width_in_mbs_minus1 */ - write_ue(out, &bits, mbs_h - 1); /* pic_height_in_map_units_minus1 */ - write_bits(out, &bits, 1, 1); /* frame_mbs_only_flag */ - write_bits(out, &bits, 1, 1); /* direct_8x8_frame_field_flag */ - write_bits(out, &bits, 0, 1); /* frame_cropping_flag */ - write_bits(out, &bits, 0, 1); /* vui_parameters_present_flag */ - write_h264_end(out, &bits, 0); - - write_bytes(out, &bits, pps, sizeof(pps)); - write_ue(out, &bits, 0); /* pic_parameter_set_id */ - write_ue(out, &bits, 0); /* seq_parameter_set_id */ - write_bits(out, &bits, 0, 1); /* entropy_coding_mode_flag */ - write_bits(out, &bits, 0, 1); /* bottom_field_pic_order_in_frame_present_flag */ - write_ue(out, &bits, 0); /* num_slice_groups_minus1 */ - write_ue(out, &bits, 0); /* num_ref_idx_l0_default_active_minus1 */ - write_ue(out, &bits, 0); /* num_ref_idx_l1_default_active_minus1 */ - write_bits(out, &bits, 0, 1); /* weighted_pred_flag */ - write_bits(out, &bits, 0, 2); /* weighted_bipred_idc */ - write_se(out, &bits, 0); /* pic_init_qp_minus26 */ - write_se(out, &bits, 0); /* pic_init_qs_minus26 */ - write_se(out, &bits, 2); /* chroma_qp_index_offset */ - write_bits(out, &bits, 0, 1); /* deblocking_filter_control_present_flag */ - write_bits(out, &bits, 1, 1); /* constrained_intra_pred_flag */ - write_bits(out, &bits, 0, 1); /* redundant_pic_cnt_present_flag */ - write_h264_end(out, &bits, 1); -} - -static int solo_fill_mpeg(struct solo_enc_fh *fh, struct solo_enc_buf *enc_buf, - struct videobuf_buffer *vb, - struct videobuf_dmabuf *vbuf) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - -#define VH_WORDS 16 -#define MAX_VOL_HEADER_LENGTH 64 - - __le32 vh[VH_WORDS]; - int ret; - int frame_size, frame_off; - int skip = 0; - - if (WARN_ON_ONCE(enc_buf->size <= sizeof(vh))) - return -EINVAL; - - /* First get the hardware vop header (not real mpeg) */ - ret = enc_get_mpeg_dma(solo_dev, vh, enc_buf->off, sizeof(vh)); - if (WARN_ON_ONCE(ret)) - return ret; - - if (WARN_ON_ONCE(vop_size(vh) > enc_buf->size)) - return -EINVAL; - - vb->width = vop_hsize(vh) << 4; - vb->height = vop_vsize(vh) << 4; - vb->size = vop_size(vh); - - /* If this is a key frame, add extra m4v header */ - if (!enc_buf->vop) { - u8 header[MAX_VOL_HEADER_LENGTH], *out = header; - - if (solo_dev->flags & FLAGS_6110) - h264_write_vol(&out, solo_dev, vh); - else - mpeg4_write_vol(&out, solo_dev, vh, - solo_dev->fps * 1000, - solo_enc->interval * 1000); - skip = out - header; - enc_write_sg(vbuf->sglist, header, skip); - /* Adjust the dma buffer past this header */ - vb->size += skip; - } - - /* Now get the actual mpeg payload */ - frame_off = (enc_buf->off + sizeof(vh)) % SOLO_MP4E_EXT_SIZE(solo_dev); - frame_size = enc_buf->size - sizeof(vh); - - ret = enc_get_mpeg_dma_sg(solo_dev, fh->desc, vbuf->sglist, - skip, frame_off, frame_size); - WARN_ON_ONCE(ret); - - return ret; -} - -static void solo_enc_fillbuf(struct solo_enc_fh *fh, - struct videobuf_buffer *vb) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct solo_enc_buf *enc_buf = NULL; - struct videobuf_dmabuf *vbuf; - int ret; - int error = 1; - u16 idx = fh->rd_idx; - - while (idx != solo_dev->enc_wr_idx) { - struct solo_enc_buf *ebuf = &solo_dev->enc_buf[idx]; - - idx = (idx + 1) % SOLO_NR_RING_BUFS; - - if (ebuf->ch != solo_enc->ch) - continue; - - if (fh->fmt == V4L2_PIX_FMT_MPEG) { - if (fh->type == ebuf->type) { - enc_buf = ebuf; - break; - } - } else { - /* For mjpeg, keep reading to the newest frame */ - enc_buf = ebuf; - } - } - - fh->rd_idx = idx; - - if (WARN_ON_ONCE(!enc_buf)) - goto buf_err; - - if ((fh->fmt == V4L2_PIX_FMT_MPEG && - vb->bsize < enc_buf->size) || - (fh->fmt == V4L2_PIX_FMT_MJPEG && - vb->bsize < (enc_buf->jpeg_size + sizeof(jpeg_header)))) { - WARN_ON_ONCE(1); - goto buf_err; - } - - vbuf = videobuf_to_dma(vb); - if (WARN_ON_ONCE(!vbuf)) - goto buf_err; - - if (fh->fmt == V4L2_PIX_FMT_MPEG) - ret = solo_fill_mpeg(fh, enc_buf, vb, vbuf); - else - ret = solo_fill_jpeg(fh, enc_buf, vb, vbuf); - - if (!ret) - error = 0; - -buf_err: - if (error) { - vb->state = VIDEOBUF_ERROR; - } else { - vb->field_count++; - vb->ts = enc_buf->ts; - vb->state = VIDEOBUF_DONE; - } - - wake_up(&vb->done); - - return; -} - -static void solo_enc_thread_try(struct solo_enc_fh *fh) -{ - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct videobuf_buffer *vb; - - for (;;) { - spin_lock(&solo_enc->lock); - - if (fh->rd_idx == solo_dev->enc_wr_idx) - break; - - if (list_empty(&fh->vidq_active)) - break; - - vb = list_first_entry(&fh->vidq_active, - struct videobuf_buffer, queue); - - if (!waitqueue_active(&vb->done)) - break; - - list_del(&vb->queue); - - spin_unlock(&solo_enc->lock); - - solo_enc_fillbuf(fh, vb); - } - - assert_spin_locked(&solo_enc->lock); - spin_unlock(&solo_enc->lock); -} - -static int solo_enc_thread(void *data) -{ - struct solo_enc_fh *fh = data; - struct solo_enc_dev *solo_enc = fh->enc; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_enc->thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_enc_thread_try(fh); - try_to_freeze(); - } - - remove_wait_queue(&solo_enc->thread_wait, &wait); - - return 0; -} - -void solo_motion_isr(struct solo_dev *solo_dev) -{ - u32 status; - int i; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_MOTION); - - status = solo_reg_read(solo_dev, SOLO_VI_MOT_STATUS); - - for (i = 0; i < solo_dev->nr_chans; i++) { - struct solo_enc_dev *solo_enc = solo_dev->v4l2_enc[i]; - - BUG_ON(solo_enc == NULL); - - if (solo_enc->motion_detected) - continue; - if (!(status & (1 << i))) - continue; - - solo_enc->motion_detected = 1; - } -} - -void solo_enc_v4l2_isr(struct solo_dev *solo_dev) -{ - struct solo_enc_buf *enc_buf; - u32 mpeg_current, mpeg_next, mpeg_size; - u32 jpeg_current, jpeg_next, jpeg_size; - u32 reg_mpeg_size; - u8 cur_q, vop_type; - u8 ch; - enum solo_enc_types enc_type; - - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_ENCODER); - - cur_q = ((solo_reg_read(solo_dev, SOLO_VE_STATE(11)) & 0xF) + 1) % MP4_QS; - - reg_mpeg_size = ((solo_reg_read(solo_dev, SOLO_VE_STATE(0)) & 0xFFFFF) + 64 + 8) & ~7; - - while (solo_dev->enc_idx != cur_q) { - mpeg_current = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - jpeg_current = solo_reg_read(solo_dev, - SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); - solo_dev->enc_idx = (solo_dev->enc_idx + 1) % MP4_QS; - mpeg_next = solo_reg_read(solo_dev, - SOLO_VE_MPEG4_QUE(solo_dev->enc_idx)); - jpeg_next = solo_reg_read(solo_dev, - SOLO_VE_JPEG_QUE(solo_dev->enc_idx)); - - ch = (mpeg_current >> 24) & 0x1f; - if (ch >= SOLO_MAX_CHANNELS) { - ch -= SOLO_MAX_CHANNELS; - enc_type = SOLO_ENC_TYPE_EXT; - } else - enc_type = SOLO_ENC_TYPE_STD; - - vop_type = (mpeg_current >> 29) & 3; - - mpeg_current &= 0x00ffffff; - mpeg_next &= 0x00ffffff; - jpeg_current &= 0x00ffffff; - jpeg_next &= 0x00ffffff; - - mpeg_size = (SOLO_MP4E_EXT_SIZE(solo_dev) + - mpeg_next - mpeg_current) % - SOLO_MP4E_EXT_SIZE(solo_dev); - - jpeg_size = (SOLO_JPEG_EXT_SIZE(solo_dev) + - jpeg_next - jpeg_current) % - SOLO_JPEG_EXT_SIZE(solo_dev); - - /* XXX I think this means we had a ring overflow? */ - if (mpeg_current > mpeg_next && mpeg_size != reg_mpeg_size) { - enc_reset_gop(solo_dev, ch); - continue; - } - - /* When resetting the GOP, skip frames until I-frame */ - if (enc_gop_reset(solo_dev, ch, vop_type)) - continue; - - enc_buf = &solo_dev->enc_buf[solo_dev->enc_wr_idx]; - - enc_buf->vop = vop_type; - enc_buf->ch = ch; - enc_buf->off = mpeg_current; - enc_buf->size = mpeg_size; - enc_buf->jpeg_off = jpeg_current; - enc_buf->jpeg_size = jpeg_size; - enc_buf->type = enc_type; - - do_gettimeofday(&enc_buf->ts); - - solo_dev->enc_wr_idx = (solo_dev->enc_wr_idx + 1) % - SOLO_NR_RING_BUFS; - - wake_up_interruptible(&solo_dev->v4l2_enc[ch]->thread_wait); - } - - return; -} - -static int solo_enc_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - *size = FRAME_BUF_SIZE; - - if (*count < MIN_VID_BUFFERS) - *count = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_enc_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct solo_enc_fh *fh = vq->priv_data; - struct solo_enc_dev *solo_enc = fh->enc; - - vb->size = FRAME_BUF_SIZE; - if (vb->baddr != 0 && vb->bsize < vb->size) - return -EINVAL; - - /* These properties only change when queue is idle */ - vb->width = solo_enc->width; - vb->height = solo_enc->height; - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int rc = videobuf_iolock(vq, vb, NULL); - if (rc < 0) { - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; - return rc; - } - } - vb->state = VIDEOBUF_PREPARED; - - return 0; -} - -static void solo_enc_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct solo_enc_fh *fh = vq->priv_data; - - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &fh->vidq_active); - wake_up_interruptible(&fh->enc->thread_wait); -} - -static void solo_enc_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops solo_enc_video_qops = { - .buf_setup = solo_enc_buf_setup, - .buf_prepare = solo_enc_buf_prepare, - .buf_queue = solo_enc_buf_queue, - .buf_release = solo_enc_buf_release, -}; - -static unsigned int solo_enc_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct solo_enc_fh *fh = file->private_data; - - return videobuf_poll_stream(file, &fh->vidq, wait); -} - -static int solo_enc_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo_enc_fh *fh = file->private_data; - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - -static int solo_enc_open(struct file *file) -{ - struct solo_enc_dev *solo_enc = video_drvdata(file); - struct solo_enc_fh *fh; - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (fh == NULL) - return -ENOMEM; - - fh->enc = solo_enc; - file->private_data = fh; - INIT_LIST_HEAD(&fh->vidq_active); - fh->fmt = V4L2_PIX_FMT_MPEG; - fh->type = SOLO_ENC_TYPE_STD; - - videobuf_queue_sg_init(&fh->vidq, &solo_enc_video_qops, - &solo_enc->solo_dev->pdev->dev, - &solo_enc->lock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_INTERLACED, - sizeof(struct videobuf_buffer), fh, NULL); - - return 0; -} - -static ssize_t solo_enc_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct solo_enc_fh *fh = file->private_data; - struct solo_enc_dev *solo_enc = fh->enc; - - /* Make sure the encoder is on */ - if (!fh->enc_on) { - int ret; - - spin_lock(&solo_enc->lock); - ret = solo_enc_on(fh); - spin_unlock(&solo_enc->lock); - if (ret) - return ret; - - ret = solo_start_fh_thread(fh); - if (ret) - return ret; - } - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static int solo_enc_release(struct file *file) -{ - struct solo_enc_fh *fh = file->private_data; - struct solo_enc_dev *solo_enc = fh->enc; - - videobuf_stop(&fh->vidq); - videobuf_mmap_free(&fh->vidq); - - spin_lock(&solo_enc->lock); - solo_enc_off(fh); - spin_unlock(&solo_enc->lock); - - kfree(fh); - - return 0; -} - -static int solo_enc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - strcpy(cap->driver, SOLO6X10_NAME); - snprintf(cap->card, sizeof(cap->card), "Softlogic 6x10 Enc %d", - solo_enc->ch); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", - pci_name(solo_dev->pdev)); - cap->version = SOLO6X10_VER_NUM; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - return 0; -} - -static int solo_enc_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - if (input->index) - return -EINVAL; - - snprintf(input->name, sizeof(input->name), "Encoder %d", - solo_enc->ch + 1); - input->type = V4L2_INPUT_TYPE_CAMERA; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - input->std = V4L2_STD_NTSC_M; - else - input->std = V4L2_STD_PAL_B; - - if (!tw28_get_video_status(solo_dev, solo_enc->ch)) - input->status = V4L2_IN_ST_NO_SIGNAL; - - return 0; -} - -static int solo_enc_set_input(struct file *file, void *priv, unsigned int index) -{ - if (index) - return -EINVAL; - - return 0; -} - -static int solo_enc_get_input(struct file *file, void *priv, - unsigned int *index) -{ - *index = 0; - - return 0; -} - -static int solo_enc_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - switch (f->index) { - case 0: - f->pixelformat = V4L2_PIX_FMT_MPEG; - strcpy(f->description, "MPEG-4 AVC"); - break; - case 1: - f->pixelformat = V4L2_PIX_FMT_MJPEG; - strcpy(f->description, "MJPEG"); - break; - default: - return -EINVAL; - } - - f->flags = V4L2_FMT_FLAG_COMPRESSED; - - return 0; -} - -static int solo_enc_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - if (pix->pixelformat != V4L2_PIX_FMT_MPEG && - pix->pixelformat != V4L2_PIX_FMT_MJPEG) - return -EINVAL; - - /* We cannot change width/height in mid read */ - if (atomic_read(&solo_enc->readers) > 0) { - if (pix->width != solo_enc->width || - pix->height != solo_enc->height) - return -EBUSY; - } - - if (pix->width < solo_dev->video_hsize || - pix->height < solo_dev->video_vsize << 1) { - /* Default to CIF 1/2 size */ - pix->width = solo_dev->video_hsize >> 1; - pix->height = solo_dev->video_vsize; - } else { - /* Full frame */ - pix->width = solo_dev->video_hsize; - pix->height = solo_dev->video_vsize << 1; - } - - if (pix->field == V4L2_FIELD_ANY) - pix->field = V4L2_FIELD_INTERLACED; - else if (pix->field != V4L2_FIELD_INTERLACED) - pix->field = V4L2_FIELD_INTERLACED; - - /* Just set these */ - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->sizeimage = FRAME_BUF_SIZE; - - return 0; -} - -static int solo_enc_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int ret; - - spin_lock(&solo_enc->lock); - - ret = solo_enc_try_fmt_cap(file, priv, f); - if (ret) { - spin_unlock(&solo_enc->lock); - return ret; - } - - if (pix->width == solo_dev->video_hsize) - solo_enc->mode = SOLO_ENC_MODE_D1; - else - solo_enc->mode = SOLO_ENC_MODE_CIF; - - /* This does not change the encoder at all */ - fh->fmt = pix->pixelformat; - - if (pix->priv) - fh->type = SOLO_ENC_TYPE_EXT; - ret = solo_enc_on(fh); - - spin_unlock(&solo_enc->lock); - - if (ret) - return ret; - - return solo_start_fh_thread(fh); -} - -static int solo_enc_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_enc->width; - pix->height = solo_enc->height; - pix->pixelformat = fh->fmt; - pix->field = solo_enc->interlaced ? V4L2_FIELD_INTERLACED : - V4L2_FIELD_NONE; - pix->sizeimage = FRAME_BUF_SIZE; - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - - return 0; -} - -static int solo_enc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_reqbufs(&fh->vidq, req); -} - -static int solo_enc_querybuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_querybuf(&fh->vidq, buf); -} - -static int solo_enc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - - return videobuf_qbuf(&fh->vidq, buf); -} - -static int solo_enc_dqbuf(struct file *file, void *priv, - struct v4l2_buffer *buf) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int ret; - - /* Make sure the encoder is on */ - if (!fh->enc_on) { - spin_lock(&solo_enc->lock); - ret = solo_enc_on(fh); - spin_unlock(&solo_enc->lock); - if (ret) - return ret; - - ret = solo_start_fh_thread(fh); - if (ret) - return ret; - } - - ret = videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); - if (ret) - return ret; - - /* Signal motion detection */ - if (solo_is_motion_on(solo_enc)) { - buf->flags |= V4L2_BUF_FLAG_MOTION_ON; - if (solo_enc->motion_detected) { - buf->flags |= V4L2_BUF_FLAG_MOTION_DETECTED; - solo_reg_write(solo_enc->solo_dev, SOLO_VI_MOT_CLEAR, - 1 << solo_enc->ch); - solo_enc->motion_detected = 0; - } - } - - /* Check for key frame on mpeg data */ - if (fh->fmt == V4L2_PIX_FMT_MPEG) { - struct videobuf_dmabuf *vbuf = - videobuf_to_dma(fh->vidq.bufs[buf->index]); - - if (vbuf) { - u8 *p = sg_virt(vbuf->sglist); - if (p[3] == 0x00) - buf->flags |= V4L2_BUF_FLAG_KEYFRAME; - else - buf->flags |= V4L2_BUF_FLAG_PFRAME; - } - } - - return 0; -} - -static int solo_enc_streamon(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct solo_enc_fh *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamon(&fh->vidq); -} - -static int solo_enc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type i) -{ - struct solo_enc_fh *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vidq); -} - -static int solo_enc_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - return 0; -} - -static int solo_enum_framesizes(struct file *file, void *priv, - struct v4l2_frmsizeenum *fsize) -{ - struct solo_enc_fh *fh = priv; - struct solo_dev *solo_dev = fh->enc->solo_dev; - - if (fsize->pixel_format != V4L2_PIX_FMT_MPEG) - return -EINVAL; - - switch (fsize->index) { - case 0: - fsize->discrete.width = solo_dev->video_hsize >> 1; - fsize->discrete.height = solo_dev->video_vsize; - break; - case 1: - fsize->discrete.width = solo_dev->video_hsize; - fsize->discrete.height = solo_dev->video_vsize << 1; - break; - default: - return -EINVAL; - } - - fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; - - return 0; -} - -static int solo_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fintv) -{ - struct solo_enc_fh *fh = priv; - struct solo_dev *solo_dev = fh->enc->solo_dev; - - if (fintv->pixel_format != V4L2_PIX_FMT_MPEG || fintv->index) - return -EINVAL; - - fintv->type = V4L2_FRMIVAL_TYPE_STEPWISE; - - fintv->stepwise.min.numerator = solo_dev->fps; - fintv->stepwise.min.denominator = 1; - - fintv->stepwise.max.numerator = solo_dev->fps; - fintv->stepwise.max.denominator = 15; - - fintv->stepwise.step.numerator = 1; - fintv->stepwise.step.denominator = 1; - - return 0; -} - -static int solo_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - cp->timeperframe.numerator = solo_enc->interval; - cp->timeperframe.denominator = solo_dev->fps; - cp->capturemode = 0; - /* XXX: Shouldn't we be able to get/set this from videobuf? */ - cp->readbuffers = 2; - - return 0; -} - -static int solo_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - struct v4l2_captureparm *cp = &sp->parm.capture; - - spin_lock(&solo_enc->lock); - - if (atomic_read(&solo_enc->readers) > 0) { - spin_unlock(&solo_enc->lock); - return -EBUSY; - } - - if ((cp->timeperframe.numerator == 0) || - (cp->timeperframe.denominator == 0)) { - /* reset framerate */ - cp->timeperframe.numerator = 1; - cp->timeperframe.denominator = solo_dev->fps; - } - - if (cp->timeperframe.denominator != solo_dev->fps) - cp->timeperframe.denominator = solo_dev->fps; - - if (cp->timeperframe.numerator > 15) - cp->timeperframe.numerator = 15; - - solo_enc->interval = cp->timeperframe.numerator; - - cp->capability = V4L2_CAP_TIMEPERFRAME; - - solo_enc->gop = max(solo_dev->fps / solo_enc->interval, 1); - solo_update_mode(solo_enc); - - spin_unlock(&solo_enc->lock); - - return 0; -} - -static int solo_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); - if (!qc->id) - return -EINVAL; - - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - return v4l2_ctrl_query_fill(qc, 0x00, 0xff, 1, 0x80); - case V4L2_CID_SHARPNESS: - return v4l2_ctrl_query_fill(qc, 0x00, 0x0f, 1, 0x00); - case V4L2_CID_MPEG_VIDEO_ENCODING: - return v4l2_ctrl_query_fill( - qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1, - V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC); - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - return v4l2_ctrl_query_fill(qc, 1, 255, 1, solo_dev->fps); -#ifdef PRIVATE_CIDS - case V4L2_CID_MOTION_THRESHOLD: - qc->flags |= V4L2_CTRL_FLAG_SLIDER; - qc->type = V4L2_CTRL_TYPE_INTEGER; - qc->minimum = 0; - qc->maximum = 0xffff; - qc->step = 1; - qc->default_value = SOLO_DEF_MOT_THRESH; - strlcpy(qc->name, "Motion Detection Threshold", - sizeof(qc->name)); - return 0; - case V4L2_CID_MOTION_ENABLE: - qc->type = V4L2_CTRL_TYPE_BOOLEAN; - qc->minimum = 0; - qc->maximum = qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "Motion Detection Enable", sizeof(qc->name)); - return 0; -#else - case V4L2_CID_MOTION_THRESHOLD: - return v4l2_ctrl_query_fill(qc, 0, 0xffff, 1, - SOLO_DEF_MOT_THRESH); - case V4L2_CID_MOTION_ENABLE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); -#endif - case V4L2_CID_RDS_TX_RADIO_TEXT: - qc->type = V4L2_CTRL_TYPE_STRING; - qc->minimum = 0; - qc->maximum = OSD_TEXT_MAX; - qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "OSD Text", sizeof(qc->name)); - return 0; - } - - return -EINVAL; -} - -static int solo_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - struct v4l2_queryctrl qctrl; - int err; - - qctrl.id = qmenu->id; - err = solo_queryctrl(file, priv, &qctrl); - if (err) - return err; - - return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL); -} - -static int solo_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_get_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - &ctrl->value); - case V4L2_CID_MPEG_VIDEO_ENCODING: - ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - ctrl->value = solo_enc->gop; - break; - case V4L2_CID_MOTION_THRESHOLD: - ctrl->value = solo_enc->motion_thresh; - break; - case V4L2_CID_MOTION_ENABLE: - ctrl->value = solo_is_motion_on(solo_enc); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int solo_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - struct solo_dev *solo_dev = solo_enc->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - case V4L2_CID_CONTRAST: - case V4L2_CID_SATURATION: - case V4L2_CID_HUE: - case V4L2_CID_SHARPNESS: - return tw28_set_ctrl_val(solo_dev, ctrl->id, solo_enc->ch, - ctrl->value); - case V4L2_CID_MPEG_VIDEO_ENCODING: - if (ctrl->value != V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC) - return -ERANGE; - break; - case V4L2_CID_MPEG_VIDEO_GOP_SIZE: - if (ctrl->value < 1 || ctrl->value > 255) - return -ERANGE; - solo_enc->gop = ctrl->value; - solo_reg_write(solo_dev, SOLO_VE_CH_GOP(solo_enc->ch), - solo_enc->gop); - solo_reg_write(solo_dev, SOLO_VE_CH_GOP_E(solo_enc->ch), - solo_enc->gop); - break; - case V4L2_CID_MOTION_THRESHOLD: - /* TODO accept value on lower 16-bits and use high - * 16-bits to assign the value to a specific block */ - if (ctrl->value < 0 || ctrl->value > 0xffff) - return -ERANGE; - solo_enc->motion_thresh = ctrl->value; - solo_set_motion_threshold(solo_dev, solo_enc->ch, ctrl->value); - break; - case V4L2_CID_MOTION_ENABLE: - solo_motion_toggle(solo_enc, ctrl->value); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int solo_s_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int i; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = (ctrls->controls + i); - int err; - - switch (ctrl->id) { - case V4L2_CID_RDS_TX_RADIO_TEXT: - if (ctrl->size - 1 > OSD_TEXT_MAX) - err = -ERANGE; - else { - err = copy_from_user(solo_enc->osd_text, - ctrl->string, - OSD_TEXT_MAX); - solo_enc->osd_text[OSD_TEXT_MAX] = '\0'; - if (!err) - err = solo_osd_print(solo_enc); - } - break; - default: - err = -EINVAL; - } - - if (err < 0) { - ctrls->error_idx = i; - return err; - } - } - - return 0; -} - -static int solo_g_ext_ctrls(struct file *file, void *priv, - struct v4l2_ext_controls *ctrls) -{ - struct solo_enc_fh *fh = priv; - struct solo_enc_dev *solo_enc = fh->enc; - int i; - - for (i = 0; i < ctrls->count; i++) { - struct v4l2_ext_control *ctrl = (ctrls->controls + i); - int err; - - switch (ctrl->id) { - case V4L2_CID_RDS_TX_RADIO_TEXT: - if (ctrl->size < OSD_TEXT_MAX) { - ctrl->size = OSD_TEXT_MAX; - err = -ENOSPC; - } else { - err = copy_to_user(ctrl->string, - solo_enc->osd_text, - OSD_TEXT_MAX); - } - break; - default: - err = -EINVAL; - } - - if (err < 0) { - ctrls->error_idx = i; - return err; - } - } - - return 0; -} - -static const struct v4l2_file_operations solo_enc_fops = { - .owner = THIS_MODULE, - .open = solo_enc_open, - .release = solo_enc_release, - .read = solo_enc_read, - .poll = solo_enc_poll, - .mmap = solo_enc_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_enc_ioctl_ops = { - .vidioc_querycap = solo_enc_querycap, - .vidioc_s_std = solo_enc_s_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enc_enum_input, - .vidioc_s_input = solo_enc_set_input, - .vidioc_g_input = solo_enc_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enc_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_enc_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_enc_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_enc_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = solo_enc_reqbufs, - .vidioc_querybuf = solo_enc_querybuf, - .vidioc_qbuf = solo_enc_qbuf, - .vidioc_dqbuf = solo_enc_dqbuf, - .vidioc_streamon = solo_enc_streamon, - .vidioc_streamoff = solo_enc_streamoff, - /* Frame size and interval */ - .vidioc_enum_framesizes = solo_enum_framesizes, - .vidioc_enum_frameintervals = solo_enum_frameintervals, - /* Video capture parameters */ - .vidioc_s_parm = solo_s_parm, - .vidioc_g_parm = solo_g_parm, - /* Controls */ - .vidioc_queryctrl = solo_queryctrl, - .vidioc_querymenu = solo_querymenu, - .vidioc_g_ctrl = solo_g_ctrl, - .vidioc_s_ctrl = solo_s_ctrl, - .vidioc_g_ext_ctrls = solo_g_ext_ctrls, - .vidioc_s_ext_ctrls = solo_s_ext_ctrls, -}; - -static struct video_device solo_enc_template = { - .name = SOLO6X10_NAME, - .fops = &solo_enc_fops, - .ioctl_ops = &solo_enc_ioctl_ops, - .minor = -1, - .release = video_device_release, - - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, - .current_norm = V4L2_STD_NTSC_M, -}; - -static struct solo_enc_dev *solo_enc_alloc(struct solo_dev *solo_dev, u8 ch) -{ - struct solo_enc_dev *solo_enc; - int ret; - - solo_enc = kzalloc(sizeof(*solo_enc), GFP_KERNEL); - if (!solo_enc) - return ERR_PTR(-ENOMEM); - - solo_enc->vfd = video_device_alloc(); - if (!solo_enc->vfd) { - kfree(solo_enc); - return ERR_PTR(-ENOMEM); - } - - solo_enc->solo_dev = solo_dev; - solo_enc->ch = ch; - - *solo_enc->vfd = solo_enc_template; - solo_enc->vfd->parent = &solo_dev->pdev->dev; - ret = video_register_device(solo_enc->vfd, VFL_TYPE_GRABBER, - video_nr); - if (ret < 0) { - video_device_release(solo_enc->vfd); - kfree(solo_enc); - return ERR_PTR(ret); - } - - video_set_drvdata(solo_enc->vfd, solo_enc); - - snprintf(solo_enc->vfd->name, sizeof(solo_enc->vfd->name), - "%s-enc (%i/%i)", SOLO6X10_NAME, solo_dev->vfd->num, - solo_enc->vfd->num); - - if (video_nr != -1) - video_nr++; - - spin_lock_init(&solo_enc->lock); - init_waitqueue_head(&solo_enc->thread_wait); - atomic_set(&solo_enc->readers, 0); - - solo_enc->qp = SOLO_DEFAULT_QP; - solo_enc->gop = solo_dev->fps; - solo_enc->interval = 1; - solo_enc->mode = SOLO_ENC_MODE_CIF; - solo_enc->motion_thresh = SOLO_DEF_MOT_THRESH; - - spin_lock(&solo_enc->lock); - solo_update_mode(solo_enc); - spin_unlock(&solo_enc->lock); - - return solo_enc; -} - -static void solo_enc_free(struct solo_enc_dev *solo_enc) -{ - if (solo_enc == NULL) - return; - - video_unregister_device(solo_enc->vfd); - kfree(solo_enc); -} - -int solo_enc_v4l2_init(struct solo_dev *solo_dev) -{ - int i; - - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_dev->v4l2_enc[i] = solo_enc_alloc(solo_dev, i); - if (IS_ERR(solo_dev->v4l2_enc[i])) - break; - } - - if (i != solo_dev->nr_chans) { - int ret = PTR_ERR(solo_dev->v4l2_enc[i]); - while (i--) - solo_enc_free(solo_dev->v4l2_enc[i]); - return ret; - } - - /* D1@MAX-FPS * 4 */ - solo_dev->enc_bw_remain = solo_dev->fps * 4 * 4; - - dev_info(&solo_dev->pdev->dev, "Encoders as /dev/video%d-%d\n", - solo_dev->v4l2_enc[0]->vfd->num, - solo_dev->v4l2_enc[solo_dev->nr_chans - 1]->vfd->num); - - return 0; -} - -void solo_enc_v4l2_exit(struct solo_dev *solo_dev) -{ - int i; - - solo_irq_off(solo_dev, SOLO_IRQ_MOTION); - - for (i = 0; i < solo_dev->nr_chans; i++) - solo_enc_free(solo_dev->v4l2_enc[i]); -} diff --git a/drivers/staging/solo6x10/v4l2.c b/drivers/staging/solo6x10/v4l2.c deleted file mode 100644 index 571c3a348d30..000000000000 --- a/drivers/staging/solo6x10/v4l2.c +++ /dev/null @@ -1,964 +0,0 @@ -/* - * Copyright (C) 2010 Bluecherry, LLC www.bluecherrydvr.com - * Copyright (C) 2010 Ben Collins - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public 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 "solo6x10.h" -#include "tw28.h" - -#define SOLO_HW_BPL 2048 -#define SOLO_DISP_PIX_FIELD V4L2_FIELD_INTERLACED - -/* Image size is two fields, SOLO_HW_BPL is one horizontal line */ -#define solo_vlines(__solo) (__solo->video_vsize * 2) -#define solo_image_size(__solo) (solo_bytesperline(__solo) * \ - solo_vlines(__solo)) -#define solo_bytesperline(__solo) (__solo->video_hsize * 2) - -#define MIN_VID_BUFFERS 4 - -/* Simple file handle */ -struct solo_filehandle { - struct solo_dev *solo_dev; - struct videobuf_queue vidq; - struct task_struct *kthread; - spinlock_t slock; - int old_write; - struct list_head vidq_active; - struct p2m_desc desc[SOLO_NR_P2M_DESC]; - int desc_idx; -}; - -unsigned video_nr = -1; -module_param(video_nr, uint, 0644); -MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect (default)"); - -static void erase_on(struct solo_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, SOLO_VO_DISP_ERASE_ON); - solo_dev->erasing = 1; - solo_dev->frame_blank = 0; -} - -static int erase_off(struct solo_dev *solo_dev) -{ - if (!solo_dev->erasing) - return 0; - - /* First time around, assert erase off */ - if (!solo_dev->frame_blank) - solo_reg_write(solo_dev, SOLO_VO_DISP_ERASE, 0); - /* Keep the erasing flag on for 8 frames minimum */ - if (solo_dev->frame_blank++ >= 8) - solo_dev->erasing = 0; - - return 1; -} - -void solo_video_in_isr(struct solo_dev *solo_dev) -{ - solo_reg_write(solo_dev, SOLO_IRQ_STAT, SOLO_IRQ_VIDEO_IN); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static void solo_win_setup(struct solo_dev *solo_dev, u8 ch, - int sx, int sy, int ex, int ey, int scale) -{ - if (ch >= solo_dev->nr_chans) - return; - - /* Here, we just keep window/channel the same */ - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL0(ch), - SOLO_VI_WIN_CHANNEL(ch) | - SOLO_VI_WIN_SX(sx) | - SOLO_VI_WIN_EX(ex) | - SOLO_VI_WIN_SCALE(scale)); - - solo_reg_write(solo_dev, SOLO_VI_WIN_CTRL1(ch), - SOLO_VI_WIN_SY(sy) | - SOLO_VI_WIN_EY(ey)); -} - -static int solo_v4l2_ch_ext_4up(struct solo_dev *solo_dev, u8 idx, int on) -{ - u8 ch = idx * 4; - - if (ch >= solo_dev->nr_chans) - return -EINVAL; - - if (!on) { - u8 i; - for (i = ch; i < ch + 4; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - /* Row 1 */ - solo_win_setup(solo_dev, ch, 0, 0, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, 3); - solo_win_setup(solo_dev, ch + 1, solo_dev->video_hsize / 2, 0, - solo_dev->video_hsize, solo_vlines(solo_dev) / 2, 3); - /* Row 2 */ - solo_win_setup(solo_dev, ch + 2, 0, solo_vlines(solo_dev) / 2, - solo_dev->video_hsize / 2, solo_vlines(solo_dev), 3); - solo_win_setup(solo_dev, ch + 3, solo_dev->video_hsize / 2, - solo_vlines(solo_dev) / 2, solo_dev->video_hsize, - solo_vlines(solo_dev), 3); - - return 0; -} - -static int solo_v4l2_ch_ext_16up(struct solo_dev *solo_dev, int on) -{ - int sy, ysize, hsize, i; - - if (!on) { - for (i = 0; i < 16; i++) - solo_win_setup(solo_dev, i, solo_dev->video_hsize, - solo_vlines(solo_dev), - solo_dev->video_hsize, - solo_vlines(solo_dev), 0); - return 0; - } - - ysize = solo_vlines(solo_dev) / 4; - hsize = solo_dev->video_hsize / 4; - - for (sy = 0, i = 0; i < 4; i++, sy += ysize) { - solo_win_setup(solo_dev, i * 4, 0, sy, hsize, - sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 1, hsize, sy, - hsize * 2, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 2, hsize * 2, sy, - hsize * 3, sy + ysize, 5); - solo_win_setup(solo_dev, (i * 4) + 3, hsize * 3, sy, - solo_dev->video_hsize, sy + ysize, 5); - } - - return 0; -} - -static int solo_v4l2_ch(struct solo_dev *solo_dev, u8 ch, int on) -{ - u8 ext_ch; - - if (ch < solo_dev->nr_chans) { - solo_win_setup(solo_dev, ch, on ? 0 : solo_dev->video_hsize, - on ? 0 : solo_vlines(solo_dev), - solo_dev->video_hsize, solo_vlines(solo_dev), - on ? 1 : 0); - return 0; - } - - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - ext_ch = ch - solo_dev->nr_chans; - - /* 4up's first */ - if (ext_ch < 4) - return solo_v4l2_ch_ext_4up(solo_dev, ext_ch, on); - - /* Remaining case is 16up for 16-port */ - return solo_v4l2_ch_ext_16up(solo_dev, on); -} - -static int solo_v4l2_set_ch(struct solo_dev *solo_dev, u8 ch) -{ - if (ch >= solo_dev->nr_chans + solo_dev->nr_ext) - return -EINVAL; - - erase_on(solo_dev); - - solo_v4l2_ch(solo_dev, solo_dev->cur_disp_ch, 0); - solo_v4l2_ch(solo_dev, ch, 1); - - solo_dev->cur_disp_ch = ch; - - return 0; -} - -static void disp_reset_desc(struct solo_filehandle *fh) -{ - /* We use desc mode, which ignores desc 0 */ - memset(fh->desc, 0, sizeof(*fh->desc)); - fh->desc_idx = 1; -} - -static int disp_flush_descs(struct solo_filehandle *fh) -{ - int ret; - - if (!fh->desc_idx) - return 0; - - ret = solo_p2m_dma_desc(fh->solo_dev, SOLO_P2M_DMA_ID_DISP, - fh->desc, fh->desc_idx); - disp_reset_desc(fh); - - return ret; -} - -static int disp_push_desc(struct solo_filehandle *fh, dma_addr_t dma_addr, - u32 ext_addr, int size, int repeat, int ext_size) -{ - if (fh->desc_idx >= SOLO_NR_P2M_DESC) { - int ret = disp_flush_descs(fh); - if (ret) - return ret; - } - - solo_p2m_push_desc(&fh->desc[fh->desc_idx], 0, dma_addr, ext_addr, - size, repeat, ext_size); - fh->desc_idx++; - - return 0; -} - -static void solo_fillbuf(struct solo_filehandle *fh, - struct videobuf_buffer *vb) -{ - struct solo_dev *solo_dev = fh->solo_dev; - struct videobuf_dmabuf *vbuf; - unsigned int fdma_addr; - int error = 1; - int i; - struct scatterlist *sg; - dma_addr_t sg_dma; - int sg_size_left; - - vbuf = videobuf_to_dma(vb); - if (!vbuf) - goto finish_buf; - - if (erase_off(solo_dev)) { - int i; - - /* Just blit to the entire sg list, ignoring size */ - for_each_sg(vbuf->sglist, sg, vbuf->sglen, i) { - void *p = sg_virt(sg); - size_t len = sg_dma_len(sg); - - for (i = 0; i < len; i += 2) { - ((u8 *)p)[i] = 0x80; - ((u8 *)p)[i + 1] = 0x00; - } - } - - error = 0; - goto finish_buf; - } - - disp_reset_desc(fh); - sg = vbuf->sglist; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - - fdma_addr = SOLO_DISP_EXT_ADDR + (fh->old_write * - (SOLO_HW_BPL * solo_vlines(solo_dev))); - - for (i = 0; i < solo_vlines(solo_dev); i++) { - int line_len = solo_bytesperline(solo_dev); - int lines; - - if (!sg_size_left) { - sg = sg_next(sg); - if (sg == NULL) - goto finish_buf; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - } - - /* No room for an entire line, so chunk it up */ - if (sg_size_left < line_len) { - int this_addr = fdma_addr; - - while (line_len > 0) { - int this_write; - - if (!sg_size_left) { - sg = sg_next(sg); - if (sg == NULL) - goto finish_buf; - sg_dma = sg_dma_address(sg); - sg_size_left = sg_dma_len(sg); - } - - this_write = min(sg_size_left, line_len); - - if (disp_push_desc(fh, sg_dma, this_addr, - this_write, 0, 0)) - goto finish_buf; - - line_len -= this_write; - sg_size_left -= this_write; - sg_dma += this_write; - this_addr += this_write; - } - - fdma_addr += SOLO_HW_BPL; - continue; - } - - /* Shove as many lines into a repeating descriptor as possible */ - lines = min(sg_size_left / line_len, - solo_vlines(solo_dev) - i); - - if (disp_push_desc(fh, sg_dma, fdma_addr, line_len, - lines - 1, SOLO_HW_BPL)) - goto finish_buf; - - i += lines - 1; - fdma_addr += SOLO_HW_BPL * lines; - sg_dma += lines * line_len; - sg_size_left -= lines * line_len; - } - - error = disp_flush_descs(fh); - -finish_buf: - if (error) { - vb->state = VIDEOBUF_ERROR; - } else { - vb->size = solo_vlines(solo_dev) * solo_bytesperline(solo_dev); - vb->state = VIDEOBUF_DONE; - vb->field_count++; - do_gettimeofday(&vb->ts); - } - - wake_up(&vb->done); - - return; -} - -static void solo_thread_try(struct solo_filehandle *fh) -{ - struct videobuf_buffer *vb; - unsigned int cur_write; - - for (;;) { - spin_lock(&fh->slock); - - if (list_empty(&fh->vidq_active)) - break; - - vb = list_first_entry(&fh->vidq_active, struct videobuf_buffer, - queue); - - if (!waitqueue_active(&vb->done)) - break; - - cur_write = SOLO_VI_STATUS0_PAGE(solo_reg_read(fh->solo_dev, - SOLO_VI_STATUS0)); - if (cur_write == fh->old_write) - break; - - fh->old_write = cur_write; - list_del(&vb->queue); - - spin_unlock(&fh->slock); - - solo_fillbuf(fh, vb); - } - - assert_spin_locked(&fh->slock); - spin_unlock(&fh->slock); -} - -static int solo_thread(void *data) -{ - struct solo_filehandle *fh = data; - struct solo_dev *solo_dev = fh->solo_dev; - DECLARE_WAITQUEUE(wait, current); - - set_freezable(); - add_wait_queue(&solo_dev->disp_thread_wait, &wait); - - for (;;) { - long timeout = schedule_timeout_interruptible(HZ); - if (timeout == -ERESTARTSYS || kthread_should_stop()) - break; - solo_thread_try(fh); - try_to_freeze(); - } - - remove_wait_queue(&solo_dev->disp_thread_wait, &wait); - - return 0; -} - -static int solo_start_thread(struct solo_filehandle *fh) -{ - fh->kthread = kthread_run(solo_thread, fh, SOLO6X10_NAME "_disp"); - - if (IS_ERR(fh->kthread)) - return PTR_ERR(fh->kthread); - - return 0; -} - -static void solo_stop_thread(struct solo_filehandle *fh) -{ - if (fh->kthread) { - kthread_stop(fh->kthread); - fh->kthread = NULL; - } -} - -static int solo_buf_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo_dev *solo_dev = fh->solo_dev; - - *size = solo_image_size(solo_dev); - - if (*count < MIN_VID_BUFFERS) - *count = MIN_VID_BUFFERS; - - return 0; -} - -static int solo_buf_prepare(struct videobuf_queue *vq, - struct videobuf_buffer *vb, enum v4l2_field field) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo_dev *solo_dev = fh->solo_dev; - - vb->size = solo_image_size(solo_dev); - if (vb->baddr != 0 && vb->bsize < vb->size) - return -EINVAL; - - /* XXX: These properties only change when queue is idle */ - vb->width = solo_dev->video_hsize; - vb->height = solo_vlines(solo_dev); - vb->bytesperline = solo_bytesperline(solo_dev); - vb->field = field; - - if (vb->state == VIDEOBUF_NEEDS_INIT) { - int rc = videobuf_iolock(vq, vb, NULL); - if (rc < 0) { - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; - return rc; - } - } - vb->state = VIDEOBUF_PREPARED; - - return 0; -} - -static void solo_buf_queue(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct solo_filehandle *fh = vq->priv_data; - struct solo_dev *solo_dev = fh->solo_dev; - - vb->state = VIDEOBUF_QUEUED; - list_add_tail(&vb->queue, &fh->vidq_active); - wake_up_interruptible(&solo_dev->disp_thread_wait); -} - -static void solo_buf_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct videobuf_dmabuf *dma = videobuf_to_dma(vb); - - videobuf_dma_unmap(vq->dev, dma); - videobuf_dma_free(dma); - vb->state = VIDEOBUF_NEEDS_INIT; -} - -static struct videobuf_queue_ops solo_video_qops = { - .buf_setup = solo_buf_setup, - .buf_prepare = solo_buf_prepare, - .buf_queue = solo_buf_queue, - .buf_release = solo_buf_release, -}; - -static unsigned int solo_v4l2_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_poll_stream(file, &fh->vidq, wait); -} - -static int solo_v4l2_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_mmap_mapper(&fh->vidq, vma); -} - -static int solo_v4l2_open(struct file *file) -{ - struct solo_dev *solo_dev = video_drvdata(file); - struct solo_filehandle *fh; - int ret; - - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (fh == NULL) - return -ENOMEM; - - spin_lock_init(&fh->slock); - INIT_LIST_HEAD(&fh->vidq_active); - fh->solo_dev = solo_dev; - file->private_data = fh; - - ret = solo_start_thread(fh); - if (ret) { - kfree(fh); - return ret; - } - - videobuf_queue_sg_init(&fh->vidq, &solo_video_qops, - &solo_dev->pdev->dev, &fh->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - SOLO_DISP_PIX_FIELD, - sizeof(struct videobuf_buffer), fh, NULL); - - return 0; -} - -static ssize_t solo_v4l2_read(struct file *file, char __user *data, - size_t count, loff_t *ppos) -{ - struct solo_filehandle *fh = file->private_data; - - return videobuf_read_stream(&fh->vidq, data, count, ppos, 0, - file->f_flags & O_NONBLOCK); -} - -static int solo_v4l2_release(struct file *file) -{ - struct solo_filehandle *fh = file->private_data; - - videobuf_stop(&fh->vidq); - videobuf_mmap_free(&fh->vidq); - solo_stop_thread(fh); - kfree(fh); - - return 0; -} - -static int solo_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - strcpy(cap->driver, SOLO6X10_NAME); - strcpy(cap->card, "Softlogic 6x10"); - snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI %s", - pci_name(solo_dev->pdev)); - cap->version = SOLO6X10_VER_NUM; - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - return 0; -} - -static int solo_enum_ext_input(struct solo_dev *solo_dev, - struct v4l2_input *input) -{ - static const char *dispnames_1[] = { "4UP" }; - static const char *dispnames_2[] = { "4UP-1", "4UP-2" }; - static const char *dispnames_5[] = { - "4UP-1", "4UP-2", "4UP-3", "4UP-4", "16UP" - }; - const char **dispnames; - - if (input->index >= (solo_dev->nr_chans + solo_dev->nr_ext)) - return -EINVAL; - - if (solo_dev->nr_ext == 5) - dispnames = dispnames_5; - else if (solo_dev->nr_ext == 2) - dispnames = dispnames_2; - else - dispnames = dispnames_1; - - snprintf(input->name, sizeof(input->name), "Multi %s", - dispnames[input->index - solo_dev->nr_chans]); - - return 0; -} - -static int solo_enum_input(struct file *file, void *priv, - struct v4l2_input *input) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - if (input->index >= solo_dev->nr_chans) { - int ret = solo_enum_ext_input(solo_dev, input); - if (ret < 0) - return ret; - } else { - snprintf(input->name, sizeof(input->name), "Camera %d", - input->index + 1); - - /* We can only check this for normal inputs */ - if (!tw28_get_video_status(solo_dev, input->index)) - input->status = V4L2_IN_ST_NO_SIGNAL; - } - - input->type = V4L2_INPUT_TYPE_CAMERA; - - if (solo_dev->video_type == SOLO_VO_FMT_TYPE_NTSC) - input->std = V4L2_STD_NTSC_M; - else - input->std = V4L2_STD_PAL_B; - - return 0; -} - -static int solo_set_input(struct file *file, void *priv, unsigned int index) -{ - struct solo_filehandle *fh = priv; - - return solo_v4l2_set_ch(fh->solo_dev, index); -} - -static int solo_get_input(struct file *file, void *priv, unsigned int *index) -{ - struct solo_filehandle *fh = priv; - - *index = fh->solo_dev->cur_disp_ch; - - return 0; -} - -static int solo_enum_fmt_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - if (f->index) - return -EINVAL; - - f->pixelformat = V4L2_PIX_FMT_UYVY; - strlcpy(f->description, "UYUV 4:2:2 Packed", sizeof(f->description)); - - return 0; -} - -static int solo_try_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - int image_size = solo_image_size(solo_dev); - - /* Check supported sizes */ - if (pix->width != solo_dev->video_hsize) - pix->width = solo_dev->video_hsize; - if (pix->height != solo_vlines(solo_dev)) - pix->height = solo_vlines(solo_dev); - if (pix->sizeimage != image_size) - pix->sizeimage = image_size; - - /* Check formats */ - if (pix->field == V4L2_FIELD_ANY) - pix->field = SOLO_DISP_PIX_FIELD; - - if (pix->pixelformat != V4L2_PIX_FMT_UYVY || - pix->field != SOLO_DISP_PIX_FIELD || - pix->colorspace != V4L2_COLORSPACE_SMPTE170M) - return -EINVAL; - - return 0; -} - -static int solo_set_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - - if (videobuf_queue_is_busy(&fh->vidq)) - return -EBUSY; - - /* For right now, if it doesn't match our running config, - * then fail */ - return solo_try_fmt_cap(file, priv, f); -} - -static int solo_get_fmt_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - struct v4l2_pix_format *pix = &f->fmt.pix; - - pix->width = solo_dev->video_hsize; - pix->height = solo_vlines(solo_dev); - pix->pixelformat = V4L2_PIX_FMT_UYVY; - pix->field = SOLO_DISP_PIX_FIELD; - pix->sizeimage = solo_image_size(solo_dev); - pix->colorspace = V4L2_COLORSPACE_SMPTE170M; - pix->bytesperline = solo_bytesperline(solo_dev); - - return 0; -} - -static int solo_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *req) -{ - struct solo_filehandle *fh = priv; - - return videobuf_reqbufs(&fh->vidq, req); -} - -static int solo_querybuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_querybuf(&fh->vidq, buf); -} - -static int solo_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_qbuf(&fh->vidq, buf); -} - -static int solo_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf) -{ - struct solo_filehandle *fh = priv; - - return videobuf_dqbuf(&fh->vidq, buf, file->f_flags & O_NONBLOCK); -} - -static int solo_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct solo_filehandle *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamon(&fh->vidq); -} - -static int solo_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct solo_filehandle *fh = priv; - - if (i != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - return videobuf_streamoff(&fh->vidq); -} - -static int solo_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - return 0; -} - -static const u32 solo_motion_ctrls[] = { - V4L2_CID_MOTION_TRACE, - 0 -}; - -static const u32 *solo_ctrl_classes[] = { - solo_motion_ctrls, - NULL -}; - -static int solo_disp_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - qc->id = v4l2_ctrl_next(solo_ctrl_classes, qc->id); - if (!qc->id) - return -EINVAL; - - switch (qc->id) { -#ifdef PRIVATE_CIDS - case V4L2_CID_MOTION_TRACE: - qc->type = V4L2_CTRL_TYPE_BOOLEAN; - qc->minimum = 0; - qc->maximum = qc->step = 1; - qc->default_value = 0; - strlcpy(qc->name, "Motion Detection Trace", sizeof(qc->name)); - return 0; -#else - case V4L2_CID_MOTION_TRACE: - return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0); -#endif - } - return -EINVAL; -} - -static int solo_disp_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - ctrl->value = solo_reg_read(solo_dev, SOLO_VI_MOTION_BAR) - ? 1 : 0; - return 0; - } - return -EINVAL; -} - -static int solo_disp_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct solo_filehandle *fh = priv; - struct solo_dev *solo_dev = fh->solo_dev; - - switch (ctrl->id) { - case V4L2_CID_MOTION_TRACE: - if (ctrl->value) { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, - SOLO_VI_MOTION_Y_ADD | - SOLO_VI_MOTION_Y_VALUE(0x20) | - SOLO_VI_MOTION_CB_VALUE(0x10) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, - SOLO_VI_MOTION_CR_ADD | - SOLO_VI_MOTION_Y_VALUE(0x10) | - SOLO_VI_MOTION_CB_VALUE(0x80) | - SOLO_VI_MOTION_CR_VALUE(0x10)); - } else { - solo_reg_write(solo_dev, SOLO_VI_MOTION_BORDER, 0); - solo_reg_write(solo_dev, SOLO_VI_MOTION_BAR, 0); - } - return 0; - } - return -EINVAL; -} - -static const struct v4l2_file_operations solo_v4l2_fops = { - .owner = THIS_MODULE, - .open = solo_v4l2_open, - .release = solo_v4l2_release, - .read = solo_v4l2_read, - .poll = solo_v4l2_poll, - .mmap = solo_v4l2_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops solo_v4l2_ioctl_ops = { - .vidioc_querycap = solo_querycap, - .vidioc_s_std = solo_s_std, - /* Input callbacks */ - .vidioc_enum_input = solo_enum_input, - .vidioc_s_input = solo_set_input, - .vidioc_g_input = solo_get_input, - /* Video capture format callbacks */ - .vidioc_enum_fmt_vid_cap = solo_enum_fmt_cap, - .vidioc_try_fmt_vid_cap = solo_try_fmt_cap, - .vidioc_s_fmt_vid_cap = solo_set_fmt_cap, - .vidioc_g_fmt_vid_cap = solo_get_fmt_cap, - /* Streaming I/O */ - .vidioc_reqbufs = solo_reqbufs, - .vidioc_querybuf = solo_querybuf, - .vidioc_qbuf = solo_qbuf, - .vidioc_dqbuf = solo_dqbuf, - .vidioc_streamon = solo_streamon, - .vidioc_streamoff = solo_streamoff, - /* Controls */ - .vidioc_queryctrl = solo_disp_queryctrl, - .vidioc_g_ctrl = solo_disp_g_ctrl, - .vidioc_s_ctrl = solo_disp_s_ctrl, -}; - -static struct video_device solo_v4l2_template = { - .name = SOLO6X10_NAME, - .fops = &solo_v4l2_fops, - .ioctl_ops = &solo_v4l2_ioctl_ops, - .minor = -1, - .release = video_device_release, - - .tvnorms = V4L2_STD_NTSC_M | V4L2_STD_PAL_B, - .current_norm = V4L2_STD_NTSC_M, -}; - -int solo_v4l2_init(struct solo_dev *solo_dev) -{ - int ret; - int i; - - init_waitqueue_head(&solo_dev->disp_thread_wait); - - solo_dev->vfd = video_device_alloc(); - if (!solo_dev->vfd) - return -ENOMEM; - - *solo_dev->vfd = solo_v4l2_template; - solo_dev->vfd->parent = &solo_dev->pdev->dev; - - ret = video_register_device(solo_dev->vfd, VFL_TYPE_GRABBER, video_nr); - if (ret < 0) { - video_device_release(solo_dev->vfd); - solo_dev->vfd = NULL; - return ret; - } - - video_set_drvdata(solo_dev->vfd, solo_dev); - - snprintf(solo_dev->vfd->name, sizeof(solo_dev->vfd->name), "%s (%i)", - SOLO6X10_NAME, solo_dev->vfd->num); - - if (video_nr != -1) - video_nr++; - - dev_info(&solo_dev->pdev->dev, "Display as /dev/video%d with " - "%d inputs (%d extended)\n", solo_dev->vfd->num, - solo_dev->nr_chans, solo_dev->nr_ext); - - /* Cycle all the channels and clear */ - for (i = 0; i < solo_dev->nr_chans; i++) { - solo_v4l2_set_ch(solo_dev, i); - while (erase_off(solo_dev)) - ;/* Do nothing */ - } - - /* Set the default display channel */ - solo_v4l2_set_ch(solo_dev, 0); - while (erase_off(solo_dev)) - ;/* Do nothing */ - - solo_irq_on(solo_dev, SOLO_IRQ_VIDEO_IN); - - return 0; -} - -void solo_v4l2_exit(struct solo_dev *solo_dev) -{ - solo_irq_off(solo_dev, SOLO_IRQ_VIDEO_IN); - if (solo_dev->vfd) { - video_unregister_device(solo_dev->vfd); - solo_dev->vfd = NULL; - } -} -- cgit v1.2.3