diff options
author | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-15 14:09:03 -0300 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2009-09-15 14:09:03 -0300 |
commit | 5c742b688922417548eae7f84f8979c6e08d9692 (patch) | |
tree | 061ccba74878c6885fe91563ae9e616b88e4a7ee /linux | |
parent | a7ec580399a117b5f3320f4e0b252d0ba87917ba (diff) | |
parent | 6c7152d8e7cab73ccc06346104d485dd0c03aa19 (diff) | |
download | mediapointer-dvb-s2-5c742b688922417548eae7f84f8979c6e08d9692.tar.gz mediapointer-dvb-s2-5c742b688922417548eae7f84f8979c6e08d9692.tar.bz2 |
merge: http://linuxtv.org/hg/~awalls/v4l-dvb
From: Mauro Carvalho Chehab <mchehab@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'linux')
116 files changed, 34541 insertions, 299 deletions
diff --git a/linux/Documentation/Docbook/Makefile b/linux/Documentation/Docbook/Makefile new file mode 100644 index 000000000..ad07875fe --- /dev/null +++ b/linux/Documentation/Docbook/Makefile @@ -0,0 +1,248 @@ +### +# This makefile is used to generate the kernel documentation, +# primarily based on in-line comments in various source files. +# See Documentation/kernel-doc-nano-HOWTO.txt for instruction in how +# to document the SRC - and how to read it. +# To add a new book the only step required is to add the book to the +# list of DOCBOOKS. + +DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \ + kernel-hacking.xml kernel-locking.xml deviceiobook.xml \ + procfs-guide.xml writing_usb_driver.xml networking.xml \ + kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \ + gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \ + genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \ + mac80211.xml debugobjects.xml sh.xml regulator.xml \ + alsa-driver-api.xml writing-an-alsa-driver.xml \ + tracepoint.xml media.xml + +### +# The build process is as follows (targets): +# (xmldocs) [by docproc] +# file.tmpl --> file.xml +--> file.ps (psdocs) [by db2ps or xmlto] +# +--> file.pdf (pdfdocs) [by db2pdf or xmlto] +# +--> DIR=file (htmldocs) [by xmlto] +# +--> man/ (mandocs) [by xmlto] + + +# for PDF and PS output you can choose between xmlto and docbook-utils tools +PDF_METHOD = $(prefer-db2x) +PS_METHOD = $(prefer-db2x) + + +### +# The targets that may be used. +PHONY += xmldocs sgmldocs psdocs pdfdocs htmldocs mandocs installmandocs cleandocs + +BOOKS := $(addprefix $(obj)/,$(DOCBOOKS)) +xmldocs: $(BOOKS) +sgmldocs: xmldocs + +PS := $(patsubst %.xml, %.ps, $(BOOKS)) +psdocs: $(PS) + +PDF := $(patsubst %.xml, %.pdf, $(BOOKS)) +pdfdocs: $(PDF) + +HTML := $(sort $(patsubst %.xml, %.html, $(BOOKS))) +htmldocs: $(HTML) + $(call build_main_index) + +MAN := $(patsubst %.xml, %.9, $(BOOKS)) +mandocs: $(MAN) + +installmandocs: mandocs + mkdir -p /usr/local/man/man9/ + install Documentation/DocBook/man/*.9.gz /usr/local/man/man9/ + +### +#External programs used +KERNELDOC = $(srctree)/scripts/kernel-doc +DOCPROC = $(objtree)/scripts/basic/docproc + +XMLTOFLAGS = -m $(srctree)/Documentation/DocBook/stylesheet.xsl +#XMLTOFLAGS += --skip-validation + +### +# DOCPROC is used for two purposes: +# 1) To generate a dependency list for a .tmpl file +# 2) To preprocess a .tmpl file and call kernel-doc with +# appropriate parameters. +# The following rules are used to generate the .xml documentation +# required to generate the final targets. (ps, pdf, html). +quiet_cmd_docproc = DOCPROC $@ + cmd_docproc = SRCTREE=$(srctree)/ $(DOCPROC) doc $< >$@ +define rule_docproc + set -e; \ + $(if $($(quiet)cmd_$(1)),echo ' $($(quiet)cmd_$(1))';) \ + $(cmd_$(1)); \ + ( \ + echo 'cmd_$@ := $(cmd_$(1))'; \ + echo $@: `SRCTREE=$(srctree) $(DOCPROC) depend $<`; \ + ) > $(dir $@).$(notdir $@).cmd +endef + +%.xml: %.tmpl FORCE + $(call if_changed_rule,docproc) + +### +#Read in all saved dependency files +cmd_files := $(wildcard $(foreach f,$(BOOKS),$(dir $(f)).$(notdir $(f)).cmd)) + +ifneq ($(cmd_files),) + include $(cmd_files) +endif + +### +# Changes in kernel-doc force a rebuild of all documentation +$(BOOKS): $(KERNELDOC) + +### +# procfs guide uses a .c file as example code. +# This requires an explicit dependency +C-procfs-example = procfs_example.xml +C-procfs-example2 = $(addprefix $(obj)/,$(C-procfs-example)) +$(obj)/procfs-guide.xml: $(C-procfs-example2) + +# List of programs to build +##oops, this is a kernel module::hostprogs-y := procfs_example +obj-m += procfs_example.o + +# Tell kbuild to always build the programs +always := $(hostprogs-y) + +notfoundtemplate = echo "*** You have to install docbook-utils or xmlto ***"; \ + exit 1 +db2xtemplate = db2TYPE -o $(dir $@) $< +xmltotemplate = xmlto TYPE $(XMLTOFLAGS) -o $(dir $@) $< + +# determine which methods are available +ifeq ($(shell which db2ps >/dev/null 2>&1 && echo found),found) + use-db2x = db2x + prefer-db2x = db2x +else + use-db2x = notfound + prefer-db2x = $(use-xmlto) +endif +ifeq ($(shell which xmlto >/dev/null 2>&1 && echo found),found) + use-xmlto = xmlto + prefer-xmlto = xmlto +else + use-xmlto = notfound + prefer-xmlto = $(use-db2x) +endif + +# the commands, generated from the chosen template +quiet_cmd_db2ps = PS $@ + cmd_db2ps = $(subst TYPE,ps, $($(PS_METHOD)template)) +%.ps : %.xml + $(call cmd,db2ps) + +quiet_cmd_db2pdf = PDF $@ + cmd_db2pdf = $(subst TYPE,pdf, $($(PDF_METHOD)template)) +%.pdf : %.xml + $(call cmd,db2pdf) + + +index = index.html +main_idx = Documentation/DocBook/$(index) +build_main_index = rm -rf $(main_idx) && \ + echo '<h1>Linux Kernel HTML Documentation</h1>' >> $(main_idx) && \ + echo '<h2>Kernel Version: $(KERNELVERSION)</h2>' >> $(main_idx) && \ + cat $(HTML) >> $(main_idx) + +quiet_cmd_db2html = HTML $@ + cmd_db2html = xmlto xhtml $(XMLTOFLAGS) -o $(patsubst %.html,%,$@) $< && \ + echo '<a HREF="$(patsubst %.html,%,$(notdir $@))/index.html"> \ + $(patsubst %.html,%,$(notdir $@))</a><p>' > $@ + +%.html: %.xml + @(which xmlto > /dev/null 2>&1) || \ + (echo "*** You need to install xmlto ***"; \ + exit 1) + @rm -rf $@ $(patsubst %.html,%,$@) + $(call cmd,db2html) + @if [ ! -z "$(PNG-$(basename $(notdir $@)))" ]; then \ + cp $(PNG-$(basename $(notdir $@))) $(patsubst %.html,%,$@); fi + +quiet_cmd_db2man = MAN $@ + cmd_db2man = if grep -q refentry $<; then xmlto man $(XMLTOFLAGS) -o $(obj)/man $< ; gzip -f $(obj)/man/*.9; fi +%.9 : %.xml + @(which xmlto > /dev/null 2>&1) || \ + (echo "*** You need to install xmlto ***"; \ + exit 1) + $(Q)mkdir -p $(obj)/man + $(call cmd,db2man) + @touch $@ + +### +# Rules to generate postscripts and PNG images from .fig format files +quiet_cmd_fig2eps = FIG2EPS $@ + cmd_fig2eps = fig2dev -Leps $< $@ + +%.eps: %.fig + @(which fig2dev > /dev/null 2>&1) || \ + (echo "*** You need to install transfig ***"; \ + exit 1) + $(call cmd,fig2eps) + +quiet_cmd_fig2png = FIG2PNG $@ + cmd_fig2png = fig2dev -Lpng $< $@ + +%.png: %.fig + @(which fig2dev > /dev/null 2>&1) || \ + (echo "*** You need to install transfig ***"; \ + exit 1) + $(call cmd,fig2png) + +### +# Rule to convert a .c file to inline XML documentation + gen_xml = : + quiet_gen_xml = echo ' GEN $@' +silent_gen_xml = : +%.xml: %.c + @$($(quiet)gen_xml) + @( \ + echo "<programlisting>"; \ + expand --tabs=8 < $< | \ + sed -e "s/&/\\&/g" \ + -e "s/</\\</g" \ + -e "s/>/\\>/g"; \ + echo "</programlisting>") > $@ + +### +# Help targets as used by the top-level makefile +dochelp: + @echo ' Linux kernel internal documentation in different formats:' + @echo ' htmldocs - HTML' + @echo ' pdfdocs - PDF' + @echo ' psdocs - Postscript' + @echo ' xmldocs - XML DocBook' + @echo ' mandocs - man pages' + @echo ' installmandocs - install man pages generated by mandocs' + @echo ' cleandocs - clean all generated DocBook files' + +### +# Temporary files left by various tools +clean-files := $(DOCBOOKS) \ + $(patsubst %.xml, %.dvi, $(DOCBOOKS)) \ + $(patsubst %.xml, %.aux, $(DOCBOOKS)) \ + $(patsubst %.xml, %.tex, $(DOCBOOKS)) \ + $(patsubst %.xml, %.log, $(DOCBOOKS)) \ + $(patsubst %.xml, %.out, $(DOCBOOKS)) \ + $(patsubst %.xml, %.ps, $(DOCBOOKS)) \ + $(patsubst %.xml, %.pdf, $(DOCBOOKS)) \ + $(patsubst %.xml, %.html, $(DOCBOOKS)) \ + $(patsubst %.xml, %.9, $(DOCBOOKS)) \ + $(C-procfs-example) $(index) + +clean-dirs := $(patsubst %.xml,%,$(DOCBOOKS)) man + +cleandocs: + $(Q)rm -f $(call objectify, $(clean-files)) + $(Q)rm -rf $(call objectify, $(clean-dirs)) + +# Declare the contents of the .PHONY variable as phony. We keep that +# information in a variable se we can use it in if_changed and friends. + +.PHONY: $(PHONY) diff --git a/linux/Documentation/dvb/get_dvb_firmware b/linux/Documentation/dvb/get_dvb_firmware index 3d1b0ab70..14b7b5a3b 100755 --- a/linux/Documentation/dvb/get_dvb_firmware +++ b/linux/Documentation/dvb/get_dvb_firmware @@ -25,7 +25,8 @@ use IO::Handle; "tda10046lifeview", "av7110", "dec2000t", "dec2540t", "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004", "or51211", "or51132_qam", "or51132_vsb", "bluebird", - "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718" ); + "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2", "mpc718", + "af9015"); # Check args syntax() if (scalar(@ARGV) != 1); @@ -514,6 +515,40 @@ sub bluebird { $outfile; } +sub af9015 { + my $sourcefile = "download.ashx?file=57"; + my $url = "http://www.ite.com.tw/EN/Services/$sourcefile"; + my $hash = "ff5b096ed47c080870eacdab2de33ad6"; + my $outfile = "dvb-usb-af9015.fw"; + my $tmpdir = tempdir(DIR => "/tmp", CLEANUP => 1); + my $fwoffset = 0x22708; + my $fwlength = 18225; + my ($chunklength, $buf, $rcount); + + checkstandard(); + + wgetfile($sourcefile, $url); + unzip($sourcefile, $tmpdir); + verify("$tmpdir/Driver/Files/AF15BDA.sys", $hash); + + open INFILE, '<', "$tmpdir/Driver/Files/AF15BDA.sys"; + open OUTFILE, '>', $outfile; + + sysseek(INFILE, $fwoffset, SEEK_SET); + while($fwlength > 0) { + $chunklength = 55; + $chunklength = $fwlength if ($chunklength > $fwlength); + $rcount = sysread(INFILE, $buf, $chunklength); + die "Ran out of data\n" if ($rcount != $chunklength); + syswrite(OUTFILE, $buf); + sysread(INFILE, $buf, 8); + $fwlength -= $rcount + 8; + } + + close OUTFILE; + close INFILE; +} + # --------------------------------------------------------------- # Utilities diff --git a/linux/Documentation/video4linux/CARDLIST.em28xx b/linux/Documentation/video4linux/CARDLIST.em28xx index b37eff3c9..b13fcbd5d 100644 --- a/linux/Documentation/video4linux/CARDLIST.em28xx +++ b/linux/Documentation/video4linux/CARDLIST.em28xx @@ -7,7 +7,7 @@ 6 -> Terratec Cinergy 200 USB (em2800) 7 -> Leadtek Winfast USB II (em2800) [0413:6023] 8 -> Kworld USB2800 (em2800) - 9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker (em2820/em2840) [1b80:e302,2304:0207,2304:021a] + 9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker (em2820/em2840) [1b80:e302,1b80:e304,2304:0207,2304:021a] 10 -> Hauppauge WinTV HVR 900 (em2880) [2040:6500] 11 -> Terratec Hybrid XS (em2880) [0ccd:0042] 12 -> Kworld PVR TV 2800 RF (em2820/em2840) diff --git a/linux/Documentation/video4linux/v4l2-framework.txt b/linux/Documentation/video4linux/v4l2-framework.txt index cb6c7eb51..b806edaf3 100644 --- a/linux/Documentation/video4linux/v4l2-framework.txt +++ b/linux/Documentation/video4linux/v4l2-framework.txt @@ -486,17 +486,27 @@ VFL_TYPE_RADIO: radioX for radio tuners VFL_TYPE_VTX: vtxX for teletext devices (deprecated, don't use) The last argument gives you a certain amount of control over the device -kernel number used (i.e. the X in videoX). Normally you will pass -1 to -let the v4l2 framework pick the first free number. But if a driver creates -many devices, then it can be useful to have different video devices in -separate ranges. For example, video capture devices start at 0, video -output devices start at 16. - -So you can use the last argument to specify a minimum kernel number and -the v4l2 framework will try to pick the first free number that is equal +device node number used (i.e. the X in videoX). Normally you will pass -1 +to let the v4l2 framework pick the first free number. But sometimes users +want to select a specific node number. It is common that drivers allow +the user to select a specific device node number through a driver module +option. That number is then passed to this function and video_register_device +will attempt to select that device node number. If that number was already +in use, then the next free device node number will be selected and it +will send a warning to the kernel log. + +Another use-case is if a driver creates many devices. In that case it can +be useful to place different video devices in separate ranges. For example, +video capture devices start at 0, video output devices start at 16. +So you can use the last argument to specify a minimum device node number +and the v4l2 framework will try to pick the first free number that is equal or higher to what you passed. If that fails, then it will just pick the first free number. +Since in this case you do not care about a warning about not being able +to select the specified device node number, you can call the function +video_register_device_no_warn() instead. + Whenever a device node is created some attributes are also created for you. If you look in /sys/class/video4linux you see the devices. Go into e.g. video0 and you will see 'name' and 'index' attributes. The 'name' attribute @@ -513,7 +523,7 @@ After the device was successfully registered, then you can use these fields: - vfl_type: the device type passed to video_register_device. - minor: the assigned device minor number. -- num: the device kernel number (i.e. the X in videoX). +- num: the device node number (i.e. the X in videoX). - index: the device index number. If the registration failed, then you need to call video_device_release() diff --git a/linux/arch/arm/mach-pxa/pcm990-baseboard.c b/linux/arch/arm/mach-pxa/pcm990-baseboard.c index 713f24c94..01791d74e 100644 --- a/linux/arch/arm/mach-pxa/pcm990-baseboard.c +++ b/linux/arch/arm/mach-pxa/pcm990-baseboard.c @@ -427,56 +427,25 @@ static void pcm990_camera_free_bus(struct soc_camera_link *link) gpio_bus_switch = -EINVAL; } +static struct soc_camera_link iclink = { + .bus_id = 0, /* Must match with the camera ID above */ + .query_bus_param = pcm990_camera_query_bus_param, + .set_bus_param = pcm990_camera_set_bus_param, + .free_bus = pcm990_camera_free_bus, +}; + /* Board I2C devices. */ static struct i2c_board_info __initdata pcm990_i2c_devices[] = { { /* Must initialize before the camera(s) */ I2C_BOARD_INFO("pca9536", 0x41), .platform_data = &pca9536_data, - }, -}; - -static struct i2c_board_info pcm990_camera_i2c[] = { - { + }, { I2C_BOARD_INFO("mt9v022", 0x48), + .platform_data = &iclink, /* With extender */ }, { I2C_BOARD_INFO("mt9m001", 0x5d), - }, -}; - -static struct soc_camera_link iclink[] = { - { - .bus_id = 0, /* Must match with the camera ID */ - .board_info = &pcm990_camera_i2c[0], - .i2c_adapter_id = 0, - .query_bus_param = pcm990_camera_query_bus_param, - .set_bus_param = pcm990_camera_set_bus_param, - .free_bus = pcm990_camera_free_bus, - .module_name = "mt9v022", - }, { - .bus_id = 0, /* Must match with the camera ID */ - .board_info = &pcm990_camera_i2c[1], - .i2c_adapter_id = 0, - .query_bus_param = pcm990_camera_query_bus_param, - .set_bus_param = pcm990_camera_set_bus_param, - .free_bus = pcm990_camera_free_bus, - .module_name = "mt9m001", - }, -}; - -static struct platform_device pcm990_camera[] = { - { - .name = "soc-camera-pdrv", - .id = 0, - .dev = { - .platform_data = &iclink[0], - }, - }, { - .name = "soc-camera-pdrv", - .id = 1, - .dev = { - .platform_data = &iclink[1], - }, + .platform_data = &iclink, /* With extender */ }, }; #endif /* CONFIG_VIDEO_PXA27x ||CONFIG_VIDEO_PXA27x_MODULE */ @@ -532,9 +501,6 @@ void __init pcm990_baseboard_init(void) pxa_set_camera_info(&pcm990_pxacamera_platform_data); i2c_register_board_info(0, ARRAY_AND_SIZE(pcm990_i2c_devices)); - - platform_device_register(&pcm990_camera[0]); - platform_device_register(&pcm990_camera[1]); #endif printk(KERN_INFO "PCM-990 Evaluation baseboard initialized\n"); diff --git a/linux/arch/sh/boards/board-ap325rxa.c b/linux/arch/sh/boards/board-ap325rxa.c index 96e5bae1c..48ab77ea0 100644 --- a/linux/arch/sh/boards/board-ap325rxa.c +++ b/linux/arch/sh/boards/board-ap325rxa.c @@ -581,7 +581,7 @@ static int __init ap325rxa_devices_setup(void) return platform_add_devices(ap325rxa_devices, ARRAY_SIZE(ap325rxa_devices)); } -device_initcall(ap325rxa_devices_setup); +arch_initcall(ap325rxa_devices_setup); /* Return the board specific boot mode pin configuration */ static int ap325rxa_mode_pins(void) diff --git a/linux/arch/sh/boards/mach-migor/setup.c b/linux/arch/sh/boards/mach-migor/setup.c index f70f4644d..f9b2e4df3 100644 --- a/linux/arch/sh/boards/mach-migor/setup.c +++ b/linux/arch/sh/boards/mach-migor/setup.c @@ -608,7 +608,7 @@ static int __init migor_devices_setup(void) return platform_add_devices(migor_devices, ARRAY_SIZE(migor_devices)); } -__initcall(migor_devices_setup); +arch_initcall(migor_devices_setup); /* Return the board specific boot mode pin configuration */ static int migor_mode_pins(void) diff --git a/linux/drivers/media/common/tuners/tda18271-common.c b/linux/drivers/media/common/tuners/tda18271-common.c index a49facb7b..4092bcf01 100644 --- a/linux/drivers/media/common/tuners/tda18271-common.c +++ b/linux/drivers/media/common/tuners/tda18271-common.c @@ -210,7 +210,8 @@ int tda18271_write_regs(struct dvb_frontend *fe, int idx, int len) tda18271_i2c_gate_ctrl(fe, 0); if (ret != 1) - tda_err("ERROR: i2c_transfer returned: %d\n", ret); + tda_err("ERROR: idx = 0x%x, len = %d, " + "i2c_transfer returned: %d\n", idx, len, ret); return (ret == 1 ? 0 : ret); } diff --git a/linux/drivers/media/common/tuners/tda18271-fe.c b/linux/drivers/media/common/tuners/tda18271-fe.c index 6c71b0a16..a3e122c89 100644 --- a/linux/drivers/media/common/tuners/tda18271-fe.c +++ b/linux/drivers/media/common/tuners/tda18271-fe.c @@ -37,6 +37,27 @@ static LIST_HEAD(hybrid_tuner_instance_list); /*---------------------------------------------------------------------*/ +static int tda18271_toggle_output(struct dvb_frontend *fe, int standby) +{ + struct tda18271_priv *priv = fe->tuner_priv; + + int ret = tda18271_set_standby_mode(fe, standby ? 1 : 0, + priv->output_opt & TDA18271_OUTPUT_LT_OFF ? 1 : 0, + priv->output_opt & TDA18271_OUTPUT_XT_OFF ? 1 : 0); + + if (tda_fail(ret)) + goto fail; + + tda_dbg("%s mode: xtal oscillator %s, slave tuner loop thru %s\n", + standby ? "standby" : "active", + priv->output_opt & TDA18271_OUTPUT_XT_OFF ? "off" : "on", + priv->output_opt & TDA18271_OUTPUT_LT_OFF ? "off" : "on"); +fail: + return ret; +} + +/*---------------------------------------------------------------------*/ + static inline int charge_pump_source(struct dvb_frontend *fe, int force) { struct tda18271_priv *priv = fe->tuner_priv; @@ -272,7 +293,7 @@ static int tda18271c2_rf_tracking_filters_correction(struct dvb_frontend *fe, tda18271_lookup_map(fe, RF_CAL_DC_OVER_DT, &freq, &dc_over_dt); /* calculate temperature compensation */ - rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal); + rfcal_comp = dc_over_dt * (tm_current - priv->tm_rfcal) / 1000; regs[R_EB14] = approx + rfcal_comp; ret = tda18271_write_regs(fe, R_EB14, 1); @@ -801,7 +822,7 @@ static int tda18271_init(struct dvb_frontend *fe) mutex_lock(&priv->lock); - /* power up */ + /* full power up */ ret = tda18271_set_standby_mode(fe, 0, 0, 0); if (tda_fail(ret)) goto fail; @@ -819,6 +840,21 @@ fail: return ret; } +static int tda18271_sleep(struct dvb_frontend *fe) +{ + struct tda18271_priv *priv = fe->tuner_priv; + int ret; + + mutex_lock(&priv->lock); + + /* enter standby mode, with required output features enabled */ + ret = tda18271_toggle_output(fe, 1); + + mutex_unlock(&priv->lock); + + return ret; +} + /* ------------------------------------------------------------------ */ static int tda18271_agc(struct dvb_frontend *fe) @@ -828,8 +864,9 @@ static int tda18271_agc(struct dvb_frontend *fe) switch (priv->config) { case 0: - /* no LNA */ - tda_dbg("no agc configuration provided\n"); + /* no external agc configuration required */ + if (tda18271_debug & DBG_ADV) + tda_dbg("no agc configuration provided\n"); break; case 3: /* switch with GPIO of saa713x */ @@ -1011,22 +1048,6 @@ fail: return ret; } -static int tda18271_sleep(struct dvb_frontend *fe) -{ - struct tda18271_priv *priv = fe->tuner_priv; - int ret; - - mutex_lock(&priv->lock); - - /* standby mode w/ slave tuner output - * & loop thru & xtal oscillator on */ - ret = tda18271_set_standby_mode(fe, 1, 0, 0); - - mutex_unlock(&priv->lock); - - return ret; -} - static int tda18271_release(struct dvb_frontend *fe) { struct tda18271_priv *priv = fe->tuner_priv; @@ -1200,6 +1221,9 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO; priv->role = (cfg) ? cfg->role : TDA18271_MASTER; priv->config = (cfg) ? cfg->config : 0; + priv->small_i2c = (cfg) ? cfg->small_i2c : 0; + priv->output_opt = (cfg) ? + cfg->output_opt : TDA18271_OUTPUT_LT_XT_ON; /* tda18271_cal_on_startup == -1 when cal * module option is unset */ @@ -1217,9 +1241,6 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, fe->tuner_priv = priv; - if (cfg) - priv->small_i2c = cfg->small_i2c; - if (tda_fail(tda18271_get_id(fe))) goto fail; @@ -1239,9 +1260,19 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr, /* existing tuner instance */ fe->tuner_priv = priv; - /* allow dvb driver to override i2c gate setting */ - if ((cfg) && (cfg->gate != TDA18271_GATE_ANALOG)) - priv->gate = cfg->gate; + /* allow dvb driver to override configuration settings */ + if (cfg) { + if (cfg->gate != TDA18271_GATE_ANALOG) + priv->gate = cfg->gate; + if (cfg->role) + priv->role = cfg->role; + if (cfg->config) + priv->config = cfg->config; + if (cfg->small_i2c) + priv->small_i2c = cfg->small_i2c; + if (cfg->output_opt) + priv->output_opt = cfg->output_opt; + } break; } diff --git a/linux/drivers/media/common/tuners/tda18271-maps.c b/linux/drivers/media/common/tuners/tda18271-maps.c index ab14ceb9e..e21fdeff3 100644 --- a/linux/drivers/media/common/tuners/tda18271-maps.c +++ b/linux/drivers/media/common/tuners/tda18271-maps.c @@ -962,10 +962,9 @@ struct tda18271_cid_target_map { static struct tda18271_cid_target_map tda18271_cid_target[] = { { .rfmax = 46000, .target = 0x04, .limit = 1800 }, { .rfmax = 52200, .target = 0x0a, .limit = 1500 }, - { .rfmax = 79100, .target = 0x01, .limit = 4000 }, + { .rfmax = 70100, .target = 0x01, .limit = 4000 }, { .rfmax = 136800, .target = 0x18, .limit = 4000 }, { .rfmax = 156700, .target = 0x18, .limit = 4000 }, - { .rfmax = 156700, .target = 0x18, .limit = 4000 }, { .rfmax = 186250, .target = 0x0a, .limit = 4000 }, { .rfmax = 230000, .target = 0x0a, .limit = 4000 }, { .rfmax = 345000, .target = 0x18, .limit = 4000 }, diff --git a/linux/drivers/media/common/tuners/tda18271-priv.h b/linux/drivers/media/common/tuners/tda18271-priv.h index e6aa1f47f..11dbc0b33 100644 --- a/linux/drivers/media/common/tuners/tda18271-priv.h +++ b/linux/drivers/media/common/tuners/tda18271-priv.h @@ -109,6 +109,7 @@ struct tda18271_priv { enum tda18271_role role; enum tda18271_i2c_gate gate; enum tda18271_ver id; + enum tda18271_output_options output_opt; unsigned int config; /* interface to saa713x / tda829x */ unsigned int tm_rfcal; diff --git a/linux/drivers/media/common/tuners/tda18271.h b/linux/drivers/media/common/tuners/tda18271.h index 71bac9593..323f29121 100644 --- a/linux/drivers/media/common/tuners/tda18271.h +++ b/linux/drivers/media/common/tuners/tda18271.h @@ -67,6 +67,17 @@ enum tda18271_i2c_gate { TDA18271_GATE_DIGITAL, }; +enum tda18271_output_options { + /* slave tuner output & loop thru & xtal oscillator always on */ + TDA18271_OUTPUT_LT_XT_ON = 0, + + /* slave tuner output loop thru off */ + TDA18271_OUTPUT_LT_OFF = 1, + + /* xtal oscillator off */ + TDA18271_OUTPUT_XT_OFF = 2, +}; + struct tda18271_config { /* override default if freq / std settings (optional) */ struct tda18271_std_map *std_map; @@ -77,6 +88,9 @@ struct tda18271_config { /* use i2c gate provided by analog or digital demod */ enum tda18271_i2c_gate gate; + /* output options that can be disabled */ + enum tda18271_output_options output_opt; + /* force rf tracking filter calibration on startup */ unsigned int rf_cal_on_startup:1; diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.c b/linux/drivers/media/common/tuners/tuner-xc2028.c index 3cf380e6b..4d31767a6 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.c +++ b/linux/drivers/media/common/tuners/tuner-xc2028.c @@ -103,6 +103,7 @@ struct xc2028_data { if (size != _rc) \ tuner_info("i2c output error: rc = %d (should be %d)\n",\ _rc, (int)size); \ + msleep(priv->ctrl.msleep); \ _rc; \ }) @@ -122,6 +123,7 @@ struct xc2028_data { if (isize != _rc) \ tuner_err("i2c input error: rc = %d (should be %d)\n", \ _rc, (int)isize); \ + msleep(priv->ctrl.msleep); \ _rc; \ }) @@ -133,7 +135,7 @@ struct xc2028_data { _val, sizeof(_val)))) { \ tuner_err("Error on line %d: %d\n", __LINE__, _rc); \ } else \ - msleep(10); \ + msleep(priv->ctrl.msleep); \ _rc; \ }) @@ -812,10 +814,20 @@ check_device: hwmodel, (version & 0xf000) >> 12, (version & 0xf00) >> 8, (version & 0xf0) >> 4, version & 0xf); + + if (priv->ctrl.read_not_reliable) + goto read_not_reliable; + /* Check firmware version against what we downloaded. */ if (priv->firm_version != ((version & 0xf0) << 4 | (version & 0x0f))) { - tuner_err("Incorrect readback of firmware version.\n"); - goto fail; + if (!priv->ctrl.read_not_reliable) { + tuner_err("Incorrect readback of firmware version.\n"); + goto fail; + } else { + tuner_err("Returned an incorrect version. However, " + "read is not reliable enough. Ignoring it.\n"); + hwmodel = 3028; + } } /* Check that the tuner hardware model remains consistent over time. */ @@ -829,6 +841,7 @@ check_device: goto fail; } +read_not_reliable: memcpy(&priv->cur_fw, &new_fw, sizeof(priv->cur_fw)); /* @@ -961,6 +974,7 @@ static int generic_set_freq(struct dvb_frontend *fe, u32 freq /* in HZ */, The reset CLK is needed only with tm6000. Driver should work fine even if this fails. */ + msleep(priv->ctrl.msleep); do_tuner_callback(fe, XC2028_RESET_CLK, 1); msleep(10); diff --git a/linux/drivers/media/common/tuners/tuner-xc2028.h b/linux/drivers/media/common/tuners/tuner-xc2028.h index a90c35d50..9778c96a5 100644 --- a/linux/drivers/media/common/tuners/tuner-xc2028.h +++ b/linux/drivers/media/common/tuners/tuner-xc2028.h @@ -33,12 +33,14 @@ enum firmware_type { struct xc2028_ctrl { char *fname; int max_len; + int msleep; unsigned int scode_table; unsigned int mts :1; unsigned int input1:1; unsigned int vhfbw7:1; unsigned int uhfbw8:1; unsigned int disable_power_mgmt:1; + unsigned int read_not_reliable:1; unsigned int demod; enum firmware_type type:2; }; diff --git a/linux/drivers/media/dvb/dvb-core/dvbdev.c b/linux/drivers/media/dvb/dvb-core/dvbdev.c index f6084e19d..2028be23d 100644 --- a/linux/drivers/media/dvb/dvb-core/dvbdev.c +++ b/linux/drivers/media/dvb/dvb-core/dvbdev.c @@ -472,6 +472,17 @@ static int dvb_uevent(struct device *dev, struct kobj_uevent_env *env) } #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +static char *dvb_nodename(struct device *dev) +{ + struct dvb_device *dvbdev = dev_get_drvdata(dev); + + return kasprintf(GFP_KERNEL, "dvb/adapter%d/%s%d", + dvbdev->adapter->num, dnames[dvbdev->type], dvbdev->id); +} + + +#endif static int __init init_dvbdev(void) { int retval; @@ -501,6 +512,9 @@ static int __init init_dvbdev(void) #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27) dvb_class->dev_uevent = dvb_uevent; #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) + dvb_class->nodename = dvb_nodename; +#endif return 0; error: diff --git a/linux/drivers/media/dvb/dvb-usb/af9015.c b/linux/drivers/media/dvb/dvb-usb/af9015.c index aeb10db0b..ab505a268 100644 --- a/linux/drivers/media/dvb/dvb-usb/af9015.c +++ b/linux/drivers/media/dvb/dvb-usb/af9015.c @@ -61,10 +61,13 @@ static struct af9013_config af9015_af9013_config[] = { static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) { +#define BUF_LEN 63 +#define REQ_HDR_LEN 8 /* send header size */ +#define ACK_HDR_LEN 2 /* rece header size */ int act_len, ret; - u8 buf[64]; + u8 buf[BUF_LEN]; u8 write = 1; - u8 msg_len = 8; + u8 msg_len = REQ_HDR_LEN; static u8 seq; /* packet sequence number */ if (mutex_lock_interruptible(&af9015_usb_mutex) < 0) @@ -94,7 +97,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) break; case WRITE_MEMORY: if (((req->addr & 0xff00) == 0xff00) || - ((req->addr & 0xae00) == 0xae00)) + ((req->addr & 0xff00) == 0xae00)) buf[0] = WRITE_VIRTUAL_MEMORY; case WRITE_VIRTUAL_MEMORY: case COPY_FIRMWARE: @@ -107,17 +110,26 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) goto error_unlock; } + /* buffer overflow check */ + if ((write && (req->data_len > BUF_LEN - REQ_HDR_LEN)) || + (!write && (req->data_len > BUF_LEN - ACK_HDR_LEN))) { + err("too much data; cmd:%d len:%d", req->cmd, req->data_len); + ret = -EINVAL; + goto error_unlock; + } + /* write requested */ - if (write && req->data_len) { - memcpy(&buf[8], req->data, req->data_len); + if (write) { + memcpy(&buf[REQ_HDR_LEN], req->data, req->data_len); msg_len += req->data_len; } + deb_xfer(">>> "); debug_dump(buf, msg_len, deb_xfer); /* send req */ ret = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 0x02), buf, msg_len, - &act_len, AF9015_USB_TIMEOUT); + &act_len, AF9015_USB_TIMEOUT); if (ret) err("bulk message failed:%d (%d/%d)", ret, msg_len, act_len); else @@ -130,10 +142,14 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) if (req->cmd == DOWNLOAD_FIRMWARE || req->cmd == RECONNECT_USB) goto exit_unlock; - /* receive ack and data if read req */ - msg_len = 1 + 1 + req->data_len; /* seq + status + data len */ + /* write receives seq + status = 2 bytes + read receives seq + status + data = 2 + N bytes */ + msg_len = ACK_HDR_LEN; + if (!write) + msg_len += req->data_len; + ret = usb_bulk_msg(udev, usb_rcvbulkpipe(udev, 0x81), buf, msg_len, - &act_len, AF9015_USB_TIMEOUT); + &act_len, AF9015_USB_TIMEOUT); if (ret) { err("recv bulk message failed:%d", ret); ret = -1; @@ -159,7 +175,7 @@ static int af9015_rw_udev(struct usb_device *udev, struct req_t *req) /* read request, copy returned data to return buf */ if (!write) - memcpy(req->data, &buf[2], req->data_len); + memcpy(req->data, &buf[ACK_HDR_LEN], req->data_len); error_unlock: exit_unlock: @@ -372,12 +388,14 @@ static int af9015_init_endpoint(struct dvb_usb_device *d) u8 packet_size; deb_info("%s: USB speed:%d\n", __func__, d->udev->speed); + /* Windows driver uses packet count 21 for USB1.1 and 348 for USB2.0. + We use smaller - about 1/4 from the original, 5 and 87. */ #define TS_PACKET_SIZE 188 -#define TS_USB20_PACKET_COUNT 348 +#define TS_USB20_PACKET_COUNT 87 #define TS_USB20_FRAME_SIZE (TS_PACKET_SIZE*TS_USB20_PACKET_COUNT) -#define TS_USB11_PACKET_COUNT 21 +#define TS_USB11_PACKET_COUNT 5 #define TS_USB11_FRAME_SIZE (TS_PACKET_SIZE*TS_USB11_PACKET_COUNT) #define TS_USB20_MAX_PACKET_SIZE 512 @@ -871,13 +889,13 @@ static int af9015_read_config(struct usb_device *udev) /* USB1.1 set smaller buffersize and disable 2nd adapter */ if (udev->speed == USB_SPEED_FULL) { af9015_properties[i].adapter[0].stream.u.bulk.buffersize - = TS_USB11_MAX_PACKET_SIZE; + = TS_USB11_FRAME_SIZE; /* disable 2nd adapter because we don't have PID-filters */ af9015_config.dual_mode = 0; } else { af9015_properties[i].adapter[0].stream.u.bulk.buffersize - = TS_USB20_MAX_PACKET_SIZE; + = TS_USB20_FRAME_SIZE; } } @@ -1313,7 +1331,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, @@ -1419,7 +1437,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, @@ -1525,7 +1543,7 @@ static struct dvb_usb_device_properties af9015_properties[] = { .u = { .bulk = { .buffersize = - TS_USB20_MAX_PACKET_SIZE, + TS_USB20_FRAME_SIZE, } } }, diff --git a/linux/drivers/media/dvb/siano/Kconfig b/linux/drivers/media/dvb/siano/Kconfig index ff297ceaf..8c1aed77e 100644 --- a/linux/drivers/media/dvb/siano/Kconfig +++ b/linux/drivers/media/dvb/siano/Kconfig @@ -28,7 +28,6 @@ config SMS_USB_DRV config SMS_SDIO_DRV tristate "SDIO interface support" depends on DVB_CORE && MMC - default m ---help--- Choose if you would like to have Siano's support for SDIO interface endmenu diff --git a/linux/drivers/media/video/cx18/cx18-driver.c b/linux/drivers/media/video/cx18/cx18-driver.c index ae13a1546..ede519748 100644 --- a/linux/drivers/media/video/cx18/cx18-driver.c +++ b/linux/drivers/media/video/cx18/cx18-driver.c @@ -231,7 +231,7 @@ MODULE_PARM_DESC(enc_pcm_bufs, "Number of encoder PCM buffers\n" "\t\t\tDefault is computed from other enc_pcm_* parameters"); -MODULE_PARM_DESC(cx18_first_minor, "Set kernel number assigned to first card"); +MODULE_PARM_DESC(cx18_first_minor, "Set device node number assigned to first card"); MODULE_AUTHOR("Hans Verkuil"); MODULE_DESCRIPTION("CX23418 driver"); diff --git a/linux/drivers/media/video/cx18/cx18-streams.c b/linux/drivers/media/video/cx18/cx18-streams.c index c134927b3..dabe3fadc 100644 --- a/linux/drivers/media/video/cx18/cx18-streams.c +++ b/linux/drivers/media/video/cx18/cx18-streams.c @@ -250,9 +250,9 @@ static int cx18_reg_dev(struct cx18 *cx, int type) video_set_drvdata(s->video_dev, s); /* Register device. First try the desired minor, then any free one. */ - ret = video_register_device(s->video_dev, vfl_type, num); + ret = video_register_device_no_warn(s->video_dev, vfl_type, num); if (ret < 0) { - CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n", + CX18_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", s->name, num); video_device_release(s->video_dev); s->video_dev = NULL; diff --git a/linux/drivers/media/video/cx23885/cx23885-dvb.c b/linux/drivers/media/video/cx23885/cx23885-dvb.c index 9c690a9bd..6333d7d18 100644 --- a/linux/drivers/media/video/cx23885/cx23885-dvb.c +++ b/linux/drivers/media/video/cx23885/cx23885-dvb.c @@ -256,15 +256,18 @@ static struct tda18271_std_map hauppauge_hvr1200_tda18271_std_map = { static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_config hauppauge_hvr1200_tuner_config = { .std_map = &hauppauge_hvr1200_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_config hauppauge_hvr1210_tuner_config = { .gate = TDA18271_GATE_DIGITAL, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda18271_std_map hauppauge_hvr127x_std_map = { @@ -276,6 +279,7 @@ static struct tda18271_std_map hauppauge_hvr127x_std_map = { static struct tda18271_config hauppauge_hvr127x_config = { .std_map = &hauppauge_hvr127x_std_map, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct lgdt3305_config hauppauge_lgdt3305_config = { diff --git a/linux/drivers/media/video/dabusb.c b/linux/drivers/media/video/dabusb.c index cb0a41c45..a0812ce91 100644 --- a/linux/drivers/media/video/dabusb.c +++ b/linux/drivers/media/video/dabusb.c @@ -813,8 +813,18 @@ static struct file_operations dabusb_fops = .release = dabusb_release, }; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) +static char *dabusb_nodename(struct device *dev) +{ + return kasprintf(GFP_KERNEL, "usb/%s", dev_name(dev)); +} + +#endif static struct usb_class_driver dabusb_class = { .name = "dabusb%d", +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31) + .nodename = dabusb_nodename, +#endif .fops = &dabusb_fops, .minor_base = DABUSB_MINOR, }; diff --git a/linux/drivers/media/video/davinci/vpif_display.c b/linux/drivers/media/video/davinci/vpif_display.c index 0f0ec8dc6..a125a452d 100644 --- a/linux/drivers/media/video/davinci/vpif_display.c +++ b/linux/drivers/media/video/davinci/vpif_display.c @@ -1422,7 +1422,7 @@ vpif_init_free_channel_objects: */ static __init int vpif_probe(struct platform_device *pdev) { - const struct subdev_info *subdevdata; + const struct vpif_subdev_info *subdevdata; int i, j = 0, k, q, m, err = 0; struct i2c_adapter *i2c_adap; struct vpif_config *config; diff --git a/linux/drivers/media/video/em28xx/Makefile b/linux/drivers/media/video/em28xx/Makefile index 8137a8c94..d0f093d1d 100644 --- a/linux/drivers/media/video/em28xx/Makefile +++ b/linux/drivers/media/video/em28xx/Makefile @@ -1,5 +1,5 @@ em28xx-objs := em28xx-video.o em28xx-i2c.o em28xx-cards.o em28xx-core.o \ - em28xx-input.o + em28xx-input.o em28xx-vbi.o em28xx-alsa-objs := em28xx-audio.o diff --git a/linux/drivers/media/video/em28xx/em28xx-cards.c b/linux/drivers/media/video/em28xx/em28xx-cards.c index d12c8c890..8fb751d5c 100644 --- a/linux/drivers/media/video/em28xx/em28xx-cards.c +++ b/linux/drivers/media/video/em28xx/em28xx-cards.c @@ -314,6 +314,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_TERRATEC_CINERGY_250] = { .name = "Terratec Cinergy 250 USB", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -333,6 +334,7 @@ struct em28xx_board em28xx_boards[] = { [EM2820_BOARD_PINNACLE_USB_2] = { .name = "Pinnacle PCTV USB 2", .tuner_type = TUNER_LG_PAL_NEW_TAPC, + .has_ir_i2c = 1, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, .input = { { @@ -357,6 +359,7 @@ struct em28xx_board em28xx_boards[] = { TDA9887_PORT2_ACTIVE, .decoder = EM28XX_TVP5150, .has_msp34xx = 1, + .has_ir_i2c = 1, .input = { { .type = EM28XX_VMUX_TELEVISION, .vmux = TVP5150_COMPOSITE0, @@ -1004,6 +1007,7 @@ struct em28xx_board em28xx_boards[] = { [EM2800_BOARD_TERRATEC_CINERGY_200] = { .name = "Terratec Cinergy 200 USB", .is_em2800 = 1, + .has_ir_i2c = 1, .tuner_type = TUNER_LG_PAL_NEW_TAPC, .tda9887_conf = TDA9887_PRESENT, .decoder = EM28XX_SAA711X, @@ -1077,7 +1081,8 @@ struct em28xx_board em28xx_boards[] = { } }, }, [EM2820_BOARD_PINNACLE_DVC_90] = { - .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker", + .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker " + "/ Kworld DVD Maker 2", .tuner_type = TUNER_ABSENT, /* capture only board */ .decoder = EM28XX_SAA711X, .input = { { @@ -1682,6 +1687,8 @@ struct usb_device_id em28xx_id_table[] = { .driver_info = EM2870_BOARD_KWORLD_355U }, { USB_DEVICE(0x1b80, 0xe302), .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */ + { USB_DEVICE(0x1b80, 0xe304), + .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kworld DVD Maker 2 */ { USB_DEVICE(0x0ccd, 0x0036), .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 }, { USB_DEVICE(0x0ccd, 0x004c), @@ -2292,12 +2299,8 @@ void em28xx_register_i2c_ir(struct em28xx *dev) /* detect & configure */ switch (dev->model) { - case (EM2800_BOARD_UNKNOWN): - break; - case (EM2820_BOARD_UNKNOWN): - break; - case (EM2800_BOARD_TERRATEC_CINERGY_200): - case (EM2820_BOARD_TERRATEC_CINERGY_250): + case EM2800_BOARD_TERRATEC_CINERGY_200: + case EM2820_BOARD_TERRATEC_CINERGY_250: #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = &ir_codes_em_terratec_table; ir->get_key = em28xx_get_key_terratec; @@ -2309,7 +2312,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.name = "i2c IR (EM28XX Terratec)"; #endif break; - case (EM2820_BOARD_PINNACLE_USB_2): + case EM2820_BOARD_PINNACLE_USB_2: #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = &ir_codes_pinnacle_grey_table; ir->get_key = em28xx_get_key_pinnacle_usb_grey; @@ -2321,7 +2324,7 @@ void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.name = "i2c IR (EM28XX Pinnacle PCTV)"; #endif break; - case (EM2820_BOARD_HAUPPAUGE_WINTV_USB_2): + case EM2820_BOARD_HAUPPAUGE_WINTV_USB_2: #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 30) ir->ir_codes = &ir_codes_hauppauge_new_table; ir->get_key = em28xx_get_key_em_haup; @@ -2333,14 +2336,6 @@ void em28xx_register_i2c_ir(struct em28xx *dev) dev->init_data.name = "i2c IR (EM2840 Hauppauge)"; #endif break; - case (EM2820_BOARD_MSI_VOX_USB_2): - break; - case (EM2800_BOARD_LEADTEK_WINFAST_USBII): - break; - case (EM2800_BOARD_KWORLD_USB2800): - break; - case (EM2800_BOARD_GRABBEEX_USB2800): - break; } #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 30) @@ -2383,7 +2378,7 @@ void em28xx_card_setup(struct em28xx *dev) case EM2883_BOARD_HAUPPAUGE_WINTV_HVR_950: { struct tveeprom tv; -#ifdef CONFIG_MODULES +#if defined(CONFIG_MODULES) && defined(MODULE) request_module("tveeprom"); #endif /* Call first TVeeprom */ @@ -2397,10 +2392,6 @@ void em28xx_card_setup(struct em28xx *dev) dev->i2s_speed = 2048000; dev->board.has_msp34xx = 1; } -#ifdef CONFIG_MODULES - if (tv.has_ir) - request_module("ir-kbd-i2c"); -#endif break; } case EM2882_BOARD_KWORLD_ATSC_315U: @@ -2441,6 +2432,10 @@ void em28xx_card_setup(struct em28xx *dev) break; } +#if defined(CONFIG_MODULES) && defined(MODULE) + if (dev->board.has_ir_i2c && !disable_ir) + request_module("ir-kbd-i2c"); +#endif if (dev->board.has_snapshot_button) em28xx_register_snapshot_button(dev); @@ -2698,7 +2693,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, * Default format, used for tvp5150 or saa711x output formats */ dev->vinmode = 0x10; - dev->vinctl = 0x11; + dev->vinctl = EM28XX_VINCTRL_INTERLACED | + EM28XX_VINCTRL_CCIR656_ENABLE; /* Do board specific init and eeprom reading */ em28xx_card_setup(dev); @@ -2717,6 +2713,8 @@ static int em28xx_init_dev(struct em28xx **devhandle, struct usb_device *udev, /* init video dma queues */ INIT_LIST_HEAD(&dev->vidq.active); INIT_LIST_HEAD(&dev->vidq.queued); + INIT_LIST_HEAD(&dev->vbiq.active); + INIT_LIST_HEAD(&dev->vbiq.queued); #if 0 video_set_drvdata(dev->vbi_dev, dev); diff --git a/linux/drivers/media/video/em28xx/em28xx-core.c b/linux/drivers/media/video/em28xx/em28xx-core.c index 46cb13182..1b9a0953e 100644 --- a/linux/drivers/media/video/em28xx/em28xx-core.c +++ b/linux/drivers/media/video/em28xx/em28xx-core.c @@ -54,6 +54,10 @@ static int alt = EM28XX_PINOUT; module_param(alt, int, 0644); MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint"); +static unsigned int disable_vbi; +module_param(disable_vbi, int, 0644); +MODULE_PARM_DESC(disable_vbi, "disable vbi support"); + /* FIXME */ #define em28xx_isocdbg(fmt, arg...) do {\ if (core_debug) \ @@ -648,9 +652,24 @@ int em28xx_capture_start(struct em28xx *dev, int start) return rc; } +int em28xx_vbi_supported(struct em28xx *dev) +{ + /* Modprobe option to manually disable */ + if (disable_vbi == 1) + return 0; + + if (dev->chip_id == CHIP_ID_EM2860 || + dev->chip_id == CHIP_ID_EM2883) + return 1; + + /* Version of em28xx that does not support VBI */ + return 0; +} + int em28xx_set_outfmt(struct em28xx *dev) { int ret; + u8 vinctrl; ret = em28xx_write_reg_bits(dev, EM28XX_R27_OUTFMT, dev->format->reg | 0x20, 0xff); @@ -661,7 +680,16 @@ int em28xx_set_outfmt(struct em28xx *dev) if (ret < 0) return ret; - return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, dev->vinctl); + vinctrl = dev->vinctl; + if (em28xx_vbi_supported(dev) == 1) { + vinctrl |= EM28XX_VINCTRL_VBI_RAW; + em28xx_write_reg(dev, EM28XX_R34_VBI_START_H, 0x00); + em28xx_write_reg(dev, EM28XX_R35_VBI_START_V, 0x09); + em28xx_write_reg(dev, EM28XX_R36_VBI_WIDTH, 0xb4); + em28xx_write_reg(dev, EM28XX_R37_VBI_HEIGHT, 0x0c); + } + + return em28xx_write_reg(dev, EM28XX_R11_VINCTRL, vinctrl); } static int em28xx_accumulator_set(struct em28xx *dev, u8 xmin, u8 xmax, @@ -732,7 +760,14 @@ int em28xx_resolution_set(struct em28xx *dev) em28xx_accumulator_set(dev, 1, (width - 4) >> 2, 1, (height - 4) >> 2); - em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); + + /* If we don't set the start position to 4 in VBI mode, we end up + with line 21 being YUYV encoded instead of being in 8-bit + greyscale */ + if (em28xx_vbi_supported(dev) == 1) + em28xx_capture_area_set(dev, 0, 4, width >> 2, height >> 2); + else + em28xx_capture_area_set(dev, 0, 0, width >> 2, height >> 2); return em28xx_scaler_set(dev, dev->hscale, dev->vscale); } @@ -853,8 +888,7 @@ static void em28xx_irq_callback(struct urb *urb, struct pt_regs *regs) static void em28xx_irq_callback(struct urb *urb) #endif { - struct em28xx_dmaqueue *dma_q = urb->context; - struct em28xx *dev = container_of(dma_q, struct em28xx, vidq); + struct em28xx *dev = urb->context; int rc, i; switch (urb->status) { @@ -939,6 +973,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, int (*isoc_copy) (struct em28xx *dev, struct urb *urb)) { struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; int i; int sb_size, pipe; struct urb *urb; @@ -968,7 +1003,8 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } dev->isoc_ctl.max_pkt_size = max_pkt_size; - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + dev->isoc_ctl.vbi_buf = NULL; sb_size = max_packets * dev->isoc_ctl.max_pkt_size; @@ -1003,7 +1039,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, usb_fill_int_urb(urb, dev->udev, pipe, dev->isoc_ctl.transfer_buffer[i], sb_size, - em28xx_irq_callback, dma_q, 1); + em28xx_irq_callback, dev, 1); urb->number_of_packets = max_packets; urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; @@ -1018,6 +1054,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets, } init_waitqueue_head(&dma_q->wq); + init_waitqueue_head(&vbi_dma_q->wq); em28xx_capture_start(dev, 1); @@ -1103,7 +1140,7 @@ struct em28xx *em28xx_get_device(int minor, list_for_each_entry(h, &em28xx_devlist, devlist) { if (h->vdev->minor == minor) dev = h; - if (h->vbi_dev->minor == minor) { + if (h->vbi_dev && h->vbi_dev->minor == minor) { dev = h; *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE; } diff --git a/linux/drivers/media/video/em28xx/em28xx-reg.h b/linux/drivers/media/video/em28xx/em28xx-reg.h index 6bf84bd78..ed12e7ffc 100644 --- a/linux/drivers/media/video/em28xx/em28xx-reg.h +++ b/linux/drivers/media/video/em28xx/em28xx-reg.h @@ -86,7 +86,19 @@ #define EM28XX_XCLK_FREQUENCY_24MHZ 0x0b #define EM28XX_R10_VINMODE 0x10 + #define EM28XX_R11_VINCTRL 0x11 + +/* em28xx Video Input Control Register 0x11 */ +#define EM28XX_VINCTRL_VBI_SLICED 0x80 +#define EM28XX_VINCTRL_VBI_RAW 0x40 +#define EM28XX_VINCTRL_VOUT_MODE_IN 0x20 /* HREF,VREF,VACT in output */ +#define EM28XX_VINCTRL_CCIR656_ENABLE 0x10 +#define EM28XX_VINCTRL_VBI_16BIT_RAW 0x08 /* otherwise 8-bit raw */ +#define EM28XX_VINCTRL_FID_ON_HREF 0x04 +#define EM28XX_VINCTRL_DUAL_EDGE_STROBE 0x02 +#define EM28XX_VINCTRL_INTERLACED 0x01 + #define EM28XX_R12_VINENABLE 0x12 /* */ #define EM28XX_R14_GAMMA 0x14 @@ -135,6 +147,10 @@ #define EM28XX_R31_HSCALEHIGH 0x31 #define EM28XX_R32_VSCALELOW 0x32 #define EM28XX_R33_VSCALEHIGH 0x33 +#define EM28XX_R34_VBI_START_H 0x34 +#define EM28XX_R35_VBI_START_V 0x35 +#define EM28XX_R36_VBI_WIDTH 0x36 +#define EM28XX_R37_VBI_HEIGHT 0x37 #define EM28XX_R40_AC97LSB 0x40 #define EM28XX_R41_AC97MSB 0x41 diff --git a/linux/drivers/media/video/em28xx/em28xx-vbi.c b/linux/drivers/media/video/em28xx/em28xx-vbi.c new file mode 100644 index 000000000..94943e5a1 --- /dev/null +++ b/linux/drivers/media/video/em28xx/em28xx-vbi.c @@ -0,0 +1,142 @@ +/* + em28xx-vbi.c - VBI driver for em28xx + + Copyright (C) 2009 Devin Heitmueller <dheitmueller@kernellabs.com> + + This work was sponsored by EyeMagnet Limited. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> + +#include "em28xx.h" + +static unsigned int vbibufs = 5; +module_param(vbibufs, int, 0644); +MODULE_PARM_DESC(vbibufs, "number of vbi buffers, range 2-32"); + +static unsigned int vbi_debug; +module_param(vbi_debug, int, 0644); +MODULE_PARM_DESC(vbi_debug, "enable debug messages [vbi]"); + +#define dprintk(level, fmt, arg...) if (vbi_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt, dev->core->name , ## arg) + +/* ------------------------------------------------------------------ */ + +static void +free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) +{ + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + unsigned long flags = 0; + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.vbi_buf == buf) + dev->isoc_ctl.vbi_buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +vbi_setup(struct videobuf_queue *q, unsigned int *count, unsigned int *size) +{ + *size = 720 * 12 * 2; + if (0 == *count) + *count = vbibufs; + if (*count < 2) + *count = 2; + if (*count > 32) + *count = 32; + return 0; +} + +static int +vbi_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + int rc = 0; + unsigned int size; + + size = 720 * 12 * 2; + + buf->vb.size = size; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + buf->vb.width = 720; + buf->vb.height = 12; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(q, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(q, buf); + return rc; +} + +static void +vbi_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, + struct em28xx_buffer, + vb); + struct em28xx_fh *fh = vq->priv_data; + struct em28xx *dev = fh->dev; + struct em28xx_dmaqueue *vbiq = &dev->vbiq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vbiq->active); +} + +static void vbi_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb); + free_buffer(q, buf); +} + +struct videobuf_queue_ops em28xx_vbi_qops = { + .buf_setup = vbi_setup, + .buf_prepare = vbi_prepare, + .buf_queue = vbi_queue, + .buf_release = vbi_release, +}; diff --git a/linux/drivers/media/video/em28xx/em28xx-video.c b/linux/drivers/media/video/em28xx/em28xx-video.c index c7d723a03..edfe795ef 100644 --- a/linux/drivers/media/video/em28xx/em28xx-video.c +++ b/linux/drivers/media/video/em28xx/em28xx-video.c @@ -181,7 +181,24 @@ static inline void buffer_filled(struct em28xx *dev, buf->vb.field_count++; do_gettimeofday(&buf->vb.ts); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +static inline void vbi_buffer_filled(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf) +{ + /* Advice that buffer was filled */ + em28xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i); + + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + dev->isoc_ctl.vbi_buf = NULL; list_del(&buf->vb.queue); wake_up(&buf->vb.done); @@ -257,7 +274,8 @@ static void em28xx_copy_video(struct em28xx *dev, if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) { - em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n", + em28xx_isocdbg("Overflow of %zi bytes past buffer end" + "(2)\n", ((char *)startwrite + lencopy) - ((char *)outp + buf->vb.size)); lencopy = remain = (char *)outp + buf->vb.size - @@ -274,6 +292,67 @@ static void em28xx_copy_video(struct em28xx *dev, dma_q->pos += len; } +static void em28xx_copy_vbi(struct em28xx *dev, + struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer *buf, + unsigned char *p, + unsigned char *outp, unsigned long len) +{ + void *startwrite, *startread; + int offset; + int bytesperline = 720; + + if (dev == NULL) { + em28xx_isocdbg("dev is null\n"); + return; + } + + if (dma_q == NULL) { + em28xx_isocdbg("dma_q is null\n"); + return; + } + if (buf == NULL) { +#if 0 + /* Disable by default - too chatty */ + em28xx_isocdbg("buf is null\n"); +#endif + return; + } + if (p == NULL) { + em28xx_isocdbg("p is null\n"); + return; + } + if (outp == NULL) { + em28xx_isocdbg("outp is null\n"); + return; + } + + if (dma_q->pos + len > buf->vb.size) + len = buf->vb.size - dma_q->pos; + + if ((p[0] == 0x33 && p[1] == 0x95) || + (p[0] == 0x88 && p[1] == 0x88)) { + /* Header field, advance past it */ + p += 4; + } else { + len += 4; + } + + startread = p; + + startwrite = outp + dma_q->pos; + offset = dma_q->pos; + + /* Make sure the bottom field populates the second half of the frame */ + if (buf->top_field == 0) { + startwrite += bytesperline * 0x0c; + offset += bytesperline * 0x0c; + } + + memcpy(startwrite, startread, len); + dma_q->pos += len; +} + static inline void print_err_status(struct em28xx *dev, int packet, int status) { @@ -326,7 +405,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, if (list_empty(&dma_q->active)) { em28xx_isocdbg("No active queue to serve\n"); - dev->isoc_ctl.buf = NULL; + dev->isoc_ctl.vid_buf = NULL; *buf = NULL; return; } @@ -340,7 +419,38 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, memset(outp, 0, (*buf)->vb.size); #endif - dev->isoc_ctl.buf = *buf; + dev->isoc_ctl.vid_buf = *buf; + + return; +} + +/* + * video-buf generic routine to get the next available VBI buffer + */ +static inline void vbi_get_next_buf(struct em28xx_dmaqueue *dma_q, + struct em28xx_buffer **buf) +{ + struct em28xx *dev = container_of(dma_q, struct em28xx, vbiq); +#if 1 + char *outp; +#endif + + if (list_empty(&dma_q->active)) { + em28xx_isocdbg("No active queue to serve\n"); + dev->isoc_ctl.vbi_buf = NULL; + *buf = NULL; + return; + } + + /* Get the next buffer */ + *buf = list_entry(dma_q->active.next, struct em28xx_buffer, vb.queue); +#if 1 + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0x00, (*buf)->vb.size); +#endif + + dev->isoc_ctl.vbi_buf = *buf; return; } @@ -351,7 +461,7 @@ static inline void get_next_buf(struct em28xx_dmaqueue *dma_q, static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) { struct em28xx_buffer *buf; - struct em28xx_dmaqueue *dma_q = urb->context; + struct em28xx_dmaqueue *dma_q = &dev->vidq; unsigned char *outp = NULL; int i, len = 0, rc = 1; unsigned char *p; @@ -368,7 +478,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return 0; } - buf = dev->isoc_ctl.buf; + buf = dev->isoc_ctl.vid_buf; if (buf != NULL) outp = videobuf_to_vmalloc(&buf->vb); @@ -432,6 +542,153 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb) return rc; } +/* Version of isoc handler that takes into account a mixture of video and + VBI data */ +static inline int em28xx_isoc_copy_vbi(struct em28xx *dev, struct urb *urb) +{ + struct em28xx_buffer *buf, *vbi_buf; + struct em28xx_dmaqueue *dma_q = &dev->vidq; + struct em28xx_dmaqueue *vbi_dma_q = &dev->vbiq; + unsigned char *outp = NULL; + unsigned char *vbioutp = NULL; + int i, len = 0, rc = 1; + unsigned char *p; + int vbi_size; + + if (!dev) + return 0; + + if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED)) + return 0; + + if (urb->status < 0) { + print_err_status(dev, -1, urb->status); + if (urb->status == -ENOENT) + return 0; + } + + buf = dev->isoc_ctl.vid_buf; + if (buf != NULL) + outp = videobuf_to_vmalloc(&buf->vb); + + vbi_buf = dev->isoc_ctl.vbi_buf; + if (vbi_buf != NULL) + vbioutp = videobuf_to_vmalloc(&vbi_buf->vb); + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status < 0) { + print_err_status(dev, i, status); + if (urb->iso_frame_desc[i].status != -EPROTO) + continue; + } + + len = urb->iso_frame_desc[i].actual_length - 4; + + if (urb->iso_frame_desc[i].actual_length <= 0) { + /* em28xx_isocdbg("packet %d is empty",i); - spammy */ + continue; + } + if (urb->iso_frame_desc[i].actual_length > + dev->max_pkt_size) { + em28xx_isocdbg("packet bigger than packet size"); + continue; + } + + p = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* capture type 0 = vbi start + capture type 1 = video start + capture type 2 = video in progress */ + if (p[0] == 0x33 && p[1] == 0x95) { + dev->capture_type = 0; + dev->vbi_read = 0; + em28xx_isocdbg("VBI START HEADER!!!\n"); + dev->cur_field = p[2]; + } + + /* FIXME: get rid of hard-coded value */ + vbi_size = 720 * 0x0c; + + if (dev->capture_type == 0) { + if (dev->vbi_read >= vbi_size) { + /* We've already read all the VBI data, so + treat the rest as video */ + em28xx_isocdbg("dev->vbi_read > vbi_size\n"); + } else if ((dev->vbi_read + len) < vbi_size) { + /* This entire frame is VBI data */ + if (dev->vbi_read == 0 && + (!(dev->cur_field & 1))) { + /* Brand new frame */ + if (vbi_buf != NULL) + vbi_buffer_filled(dev, + vbi_dma_q, + vbi_buf); + vbi_get_next_buf(vbi_dma_q, &vbi_buf); + if (vbi_buf == NULL) + vbioutp = NULL; + else + vbioutp = videobuf_to_vmalloc( + &vbi_buf->vb); + } + + if (dev->vbi_read == 0) { + vbi_dma_q->pos = 0; + if (vbi_buf != NULL) { + if (dev->cur_field & 1) + vbi_buf->top_field = 0; + else + vbi_buf->top_field = 1; + } + } + + dev->vbi_read += len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, len); + } else { + /* Some of this frame is VBI data and some is + video data */ + int vbi_data_len = vbi_size - dev->vbi_read; + dev->vbi_read += vbi_data_len; + em28xx_copy_vbi(dev, vbi_dma_q, vbi_buf, p, + vbioutp, vbi_data_len); + dev->capture_type = 1; + p += vbi_data_len; + len -= vbi_data_len; + } + } + + if (dev->capture_type == 1) { + dev->capture_type = 2; + em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2], + len, (p[2] & 1) ? "odd" : "even"); + + if (dev->progressive || !(dev->cur_field & 1)) { + if (buf != NULL) + buffer_filled(dev, dma_q, buf); + get_next_buf(dma_q, &buf); + if (buf == NULL) + outp = NULL; + else + outp = videobuf_to_vmalloc(&buf->vb); + } + if (buf != NULL) { + if (dev->cur_field & 1) + buf->top_field = 0; + else + buf->top_field = 1; + } + + dma_q->pos = 0; + } + if (buf != NULL && dev->capture_type == 2) + em28xx_copy_video(dev, dma_q, buf, p, outp, len); + } + return rc; +} + + /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ @@ -443,7 +700,8 @@ buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) struct em28xx *dev = fh->dev; struct v4l2_frequency f; - *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) + >> 3; if (0 == *count) *count = EM28XX_DEF_BUF; @@ -480,8 +738,8 @@ static void free_buffer(struct videobuf_queue *vq, struct em28xx_buffer *buf) VIDEOBUF_ACTIVE, it won't be, though. */ spin_lock_irqsave(&dev->slock, flags); - if (dev->isoc_ctl.buf == buf) - dev->isoc_ctl.buf = NULL; + if (dev->isoc_ctl.vid_buf == buf) + dev->isoc_ctl.vid_buf = NULL; spin_unlock_irqrestore(&dev->slock, flags); videobuf_vmalloc_free(&buf->vb); @@ -497,7 +755,8 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, struct em28xx *dev = fh->dev; int rc = 0, urb_init = 0; - buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + 7) >> 3; + buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth + + 7) >> 3; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; @@ -516,9 +775,16 @@ buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, urb_init = 1; if (urb_init) { - rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, - EM28XX_NUM_BUFS, dev->max_pkt_size, - em28xx_isoc_copy); + if (em28xx_vbi_supported(dev) == 1) + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy_vbi); + else + rc = em28xx_init_isoc(dev, EM28XX_NUM_PACKETS, + EM28XX_NUM_BUFS, + dev->max_pkt_size, + em28xx_isoc_copy); if (rc < 0) goto fail; } @@ -600,34 +866,63 @@ static void video_mux(struct em28xx *dev, int index) } /* Usage lock check functions */ -static int res_get(struct em28xx_fh *fh) +static int res_get(struct em28xx_fh *fh, unsigned int bit) { struct em28xx *dev = fh->dev; - int rc = 0; - /* This instance already has stream_on */ - if (fh->stream_on) - return rc; + if (fh->resources & bit) + /* have it already allocated */ + return 1; - if (dev->stream_on) - return -EBUSY; + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + em28xx_videodbg("res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} - dev->stream_on = 1; - fh->stream_on = 1; - return rc; +static int res_check(struct em28xx_fh *fh, unsigned int bit) +{ + return fh->resources & bit; } -static int res_check(struct em28xx_fh *fh) +static int res_locked(struct em28xx *dev, unsigned int bit) { - return fh->stream_on; + return dev->resources & bit; } -static void res_free(struct em28xx_fh *fh) +static void res_free(struct em28xx_fh *fh, unsigned int bits) { struct em28xx *dev = fh->dev; - fh->stream_on = 0; - dev->stream_on = 0; + BUG_ON((fh->resources & bits) != bits); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + em28xx_videodbg("res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +static int get_ressource(struct em28xx_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return EM28XX_RESOURCE_VIDEO; + case V4L2_BUF_TYPE_VBI_CAPTURE: + return EM28XX_RESOURCE_VBI; + default: + BUG(); + return 0; + } } /* @@ -804,7 +1099,8 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, } else { /* width must even because of the YUYV format height must be even because of interlacing */ - v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, 1, 0); + v4l_bound_align_image(&width, 48, maxw, 1, &height, 32, maxh, + 1, 0); } get_scale(dev, width, height, &hscale, &vscale); @@ -870,12 +1166,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, goto out; } - if (dev->stream_on && !fh->stream_on) { - em28xx_errdev("%s device in use by another fh\n", __func__); - rc = -EBUSY; - goto out; - } - rc = em28xx_set_video_format(dev, f->fmt.pix.pixelformat, f->fmt.pix.width, f->fmt.pix.height); @@ -884,6 +1174,21 @@ out: return rc; } +static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id *norm) +{ + struct em28xx_fh *fh = priv; + struct em28xx *dev = fh->dev; + int rc; + + rc = check_dev(dev); + if (rc < 0) + return rc; + + *norm = dev->norm; + + return 0; +} + static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm) { struct em28xx_fh *fh = priv; @@ -1441,20 +1746,25 @@ static int vidioc_streamon(struct file *file, void *priv, { struct em28xx_fh *fh = priv; struct em28xx *dev = fh->dev; - int rc; + int rc = -EINVAL; rc = check_dev(dev); if (rc < 0) return rc; + if (unlikely(type != fh->type)) + return -EINVAL; - mutex_lock(&dev->lock); - rc = res_get(fh); + em28xx_videodbg("vidioc_streamon fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - if (likely(rc >= 0)) - rc = videobuf_streamon(&fh->vb_vidq); + if (unlikely(!res_get(fh, get_ressource(fh)))) + return -EBUSY; - mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_streamon(&fh->vb_vidq); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_streamon(&fh->vb_vbiq); return rc; } @@ -1470,17 +1780,22 @@ static int vidioc_streamoff(struct file *file, void *priv, if (rc < 0) return rc; - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fh->type != V4L2_BUF_TYPE_VBI_CAPTURE) return -EINVAL; if (type != fh->type) return -EINVAL; - mutex_lock(&dev->lock); - - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); + em28xx_videodbg("vidioc_streamoff fh=%p t=%d fh->res=%d dev->res=%d\n", + fh, type, fh->resources, dev->resources); - mutex_unlock(&dev->lock); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + videobuf_streamoff(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + videobuf_streamoff(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } return 0; } @@ -1498,13 +1813,13 @@ static int vidioc_querycap(struct file *file, void *priv, cap->version = EM28XX_VERSION_CODE; cap->capabilities = -#if 0 - V4L2_CAP_VBI_CAPTURE | -#endif V4L2_CAP_SLICED_VBI_CAPTURE | V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (dev->vbi_dev) + cap->capabilities |= V4L2_CAP_VBI_CAPTURE; + if (dev->audio_mode.has_audio) cap->capabilities |= V4L2_CAP_AUDIO; @@ -1572,40 +1887,45 @@ static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv, return 0; } -#if 0 /* RAW VBI ioctls */ static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) + struct v4l2_format *format) { - format->fmt.vbi.sampling_rate = 6750000 * 4; - format->fmt.vbi.samples_per_line = 2048 /* VBI_LINE_LENGTH */; + format->fmt.vbi.samples_per_line = 720; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 64 * 4; - format->fmt.vbi.start[0] = norm->vbi_v_start_0; - format->fmt.vbi.count[0] = norm->vbi_v_stop_0 - norm->vbi_v_start_0 + 1; - format->fmt.vbi.start[1] = norm->vbi_v_start_1; - format->fmt.vbi.count[1] = format->fmt.vbi.count[0]; - format->fmt.vbi.flags = 0; /* VBI_UNSYNC VBI_INTERLACED */ + format->fmt.vbi.offset = 0; + format->fmt.vbi.flags = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; return 0; } -static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv, - struct v4l2_format *f) +static int vidioc_s_fmt_vbi_cap(struct file *file, void *priv, + struct v4l2_format *format) { - format->type = V4L2_BUF_TYPE_VBI_CAPTURE; - format->fmt.vbi.sampling_rate = HZ; - format->fmt.vbi.samples_per_line = 2048; + format->fmt.vbi.samples_per_line = 720; format->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY; - format->fmt.vbi.offset = 244; + format->fmt.vbi.offset = 0; format->fmt.vbi.flags = 0; - format->fmt.vbi.start[0] = 0; - format->fmt.vbi.start[1] = 0; + + /* Varies by video standard (NTSC, PAL, etc.) */ + /* FIXME: hard-coded for NTSC support */ + format->fmt.vbi.sampling_rate = 6750000 * 4 / 2; /* FIXME: ??? */ + format->fmt.vbi.count[0] = 12; + format->fmt.vbi.count[1] = 12; + format->fmt.vbi.start[0] = 10; + format->fmt.vbi.start[1] = 273; return 0; } -#endif static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) @@ -1618,7 +1938,10 @@ static int vidioc_reqbufs(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_reqbufs(&fh->vb_vidq, rb); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_reqbufs(&fh->vb_vidq, rb); + else + return videobuf_reqbufs(&fh->vb_vbiq, rb); } static int vidioc_querybuf(struct file *file, void *priv, @@ -1632,7 +1955,18 @@ static int vidioc_querybuf(struct file *file, void *priv, if (rc < 0) return rc; - return videobuf_querybuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_querybuf(&fh->vb_vidq, b); + else { + /* FIXME: I'm not sure yet whether this is a bug in zvbi or + the videobuf framework, but we probably shouldn't be + returning a buffer larger than that which was asked for. + At a minimum, it causes a crash in zvbi since it does + a memcpy based on the source buffer length */ + int result = videobuf_querybuf(&fh->vb_vbiq, b); + b->length = 17280; + return result; + } } static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1645,7 +1979,10 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_qbuf(&fh->vb_vidq, b); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_qbuf(&fh->vb_vidq, b); + else + return videobuf_qbuf(&fh->vb_vbiq, b); } static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) @@ -1658,7 +1995,12 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b) if (rc < 0) return rc; - return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & + O_NONBLOCK); + else + return videobuf_dqbuf(&fh->vb_vbiq, b, file->f_flags & + O_NONBLOCK); } #ifdef CONFIG_VIDEO_V4L1_COMPAT @@ -1666,7 +2008,10 @@ static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) { struct em28xx_fh *fh = priv; - return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8); + else + return videobuf_cgmbuf(&fh->vb_vbiq, mbuf, 8); } #endif @@ -1798,7 +2143,7 @@ static int em28xx_v4l2_open(struct file *filp) #if 0 errCode = em28xx_set_mode(dev, EM28XX_ANALOG_MODE); if (errCode < 0) { - em28xx_errdev("Device locked on digital mode. Can't open analog\n"); + em28xx_errdev("Locked on digital mode. Can't open analog\n"); mutex_unlock(&dev->lock); return -EBUSY; } @@ -1846,8 +2191,15 @@ static int em28xx_v4l2_open(struct file *filp) field = V4L2_FIELD_INTERLACED; videobuf_queue_vmalloc_init(&fh->vb_vidq, &em28xx_video_qops, - NULL, &dev->slock, fh->type, field, - sizeof(struct em28xx_buffer), fh); + NULL, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, field, + sizeof(struct em28xx_buffer), fh); + + videobuf_queue_vmalloc_init(&fh->vb_vbiq, &em28xx_vbi_qops, + NULL, &dev->slock, + V4L2_BUF_TYPE_VBI_CAPTURE, + V4L2_FIELD_SEQ_TB, + sizeof(struct em28xx_buffer), fh); mutex_unlock(&dev->lock); @@ -1904,20 +2256,21 @@ static int em28xx_v4l2_close(struct file *filp) em28xx_videodbg("users=%d\n", dev->users); + if (res_check(fh, EM28XX_RESOURCE_VIDEO)) { + videobuf_stop(&fh->vb_vidq); + res_free(fh, EM28XX_RESOURCE_VIDEO); + } - mutex_lock(&dev->lock); - if (res_check(fh)) - res_free(fh); + if (res_check(fh, EM28XX_RESOURCE_VBI)) { + videobuf_stop(&fh->vb_vbiq); + res_free(fh, EM28XX_RESOURCE_VBI); + } if (dev->users == 1) { - videobuf_stop(&fh->vb_vidq); - videobuf_mmap_free(&fh->vb_vidq); - /* the device is already disconnect, free the remaining resources */ if (dev->state & DEV_DISCONNECTED) { em28xx_release_resources(dev); - mutex_unlock(&dev->lock); kfree(dev); return 0; } @@ -1938,10 +2291,12 @@ static int em28xx_v4l2_close(struct file *filp) "0 (error=%i)\n", errCode); } } + + videobuf_mmap_free(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vbiq); kfree(fh); dev->users--; wake_up_interruptible_nr(&dev->open, 1); - mutex_unlock(&dev->lock); return 0; } @@ -1966,16 +2321,22 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count, */ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; + if (res_locked(dev, EM28XX_RESOURCE_VIDEO)) + return -EBUSY; return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0, filp->f_flags & O_NONBLOCK); } + + + if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vbiq, buf, count, pos, 0, + filp->f_flags & O_NONBLOCK); + } + return 0; } @@ -1993,17 +2354,17 @@ static unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return POLLERR; - - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VIDEO)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) { + if (!res_get(fh, EM28XX_RESOURCE_VBI)) + return POLLERR; + return videobuf_poll_stream(filp, &fh->vb_vbiq, wait); + } else { return POLLERR; - - return videobuf_poll_stream(filp, &fh->vb_vidq, wait); + } } /* @@ -2019,14 +2380,10 @@ static int em28xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma) if (rc < 0) return rc; - mutex_lock(&dev->lock); - rc = res_get(fh); - mutex_unlock(&dev->lock); - - if (unlikely(rc < 0)) - return rc; - - rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vidq, vma); + else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) + rc = videobuf_mmap_mapper(&fh->vb_vbiq, vma); em28xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n", (unsigned long)vma->vm_start, @@ -2052,11 +2409,8 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .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, -#if 0 .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap, - .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap, -#endif .vidioc_g_audio = vidioc_g_audio, .vidioc_s_audio = vidioc_s_audio, .vidioc_cropcap = vidioc_cropcap, @@ -2069,6 +2423,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .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_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, @@ -2190,15 +2545,10 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->mute = 1; dev->volume = 0x1f; -#if 1 - /* enable vbi capturing */ - /* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */ val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK); em28xx_write_reg(dev, EM28XX_R0F_XCLK, (EM28XX_XCLK_AUDIO_UNMUTE | val)); - em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51); -#endif em28xx_set_outfmt(dev); em28xx_colorlevels_set_default(dev); @@ -2221,14 +2571,17 @@ int em28xx_register_analog_devices(struct em28xx *dev) } /* Allocate and fill vbi video_device struct */ - dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, "vbi"); + if (em28xx_vbi_supported(dev) == 1) { + dev->vbi_dev = em28xx_vdev_init(dev, &em28xx_video_template, + "vbi"); - /* register v4l2 vbi video_device */ - ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, - vbi_nr[dev->devno]); - if (ret < 0) { - em28xx_errdev("unable to register vbi device\n"); - return ret; + /* register v4l2 vbi video_device */ + ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, + vbi_nr[dev->devno]); + if (ret < 0) { + em28xx_errdev("unable to register vbi device\n"); + return ret; + } } if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) { @@ -2248,8 +2601,12 @@ int em28xx_register_analog_devices(struct em28xx *dev) dev->radio_dev->num); } - em28xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n", - dev->vdev->num, dev->vbi_dev->num); + em28xx_info("V4L2 video device registered as /dev/video%d\n", + dev->vdev->num); + + if (dev->vbi_dev) + em28xx_info("V4L2 VBI device registered as /dev/vbi%d\n", + dev->vbi_dev->num); return 0; } diff --git a/linux/drivers/media/video/em28xx/em28xx.h b/linux/drivers/media/video/em28xx/em28xx.h index 2d0aa6edb..90e9e2fb5 100644 --- a/linux/drivers/media/video/em28xx/em28xx.h +++ b/linux/drivers/media/video/em28xx/em28xx.h @@ -215,7 +215,8 @@ struct em28xx_usb_isoc_ctl { int tmp_buf_len; /* Stores already requested buffers */ - struct em28xx_buffer *buf; + struct em28xx_buffer *vid_buf; + struct em28xx_buffer *vbi_buf; /* Stores the number of received fields */ int nfields; @@ -401,6 +402,7 @@ struct em28xx_board { unsigned int is_webcam:1; unsigned int no_audio:1; unsigned int valid:1; + unsigned int has_ir_i2c:1; unsigned char xclk, i2c_speed; unsigned char radio_addr; @@ -444,6 +446,10 @@ enum em28xx_dev_state { #define EM28XX_AUDIO 0x10 #define EM28XX_DVB 0x20 +/* em28xx resource types (used for res_get/res_lock etc */ +#define EM28XX_RESOURCE_VIDEO 0x01 +#define EM28XX_RESOURCE_VBI 0x02 + struct em28xx_audio { char name[50]; char *transfer_buffer[EM28XX_AUDIO_BUFS]; @@ -472,10 +478,11 @@ struct em28xx; struct em28xx_fh { struct em28xx *dev; - unsigned int stream_on:1; /* Locks streams */ int radio; + unsigned int resources; struct videobuf_queue vb_vidq; + struct videobuf_queue vb_vbiq; enum v4l2_buf_type type; }; @@ -502,7 +509,6 @@ struct em28xx { /* Vinmode/Vinctl used at the driver */ int vinmode, vinctl; - unsigned int stream_on:1; /* Locks streams */ unsigned int has_audio_class:1; unsigned int has_alsa_audio:1; @@ -553,6 +559,12 @@ struct em28xx { enum em28xx_dev_state state; enum em28xx_io_method io; + /* vbi related state tracking */ + int capture_type; + int vbi_read; + unsigned char cur_field; + + struct work_struct request_module_wk; /* locks */ @@ -564,10 +576,14 @@ struct em28xx { struct video_device *vbi_dev; struct video_device *radio_dev; + /* resources in use */ + unsigned int resources; + unsigned char eedata[256]; /* Isoc control struct */ struct em28xx_dmaqueue vidq; + struct em28xx_dmaqueue vbiq; struct em28xx_usb_isoc_ctl isoc_ctl; spinlock_t slock; @@ -650,6 +666,7 @@ int em28xx_audio_setup(struct em28xx *dev); int em28xx_colorlevels_set_default(struct em28xx *dev); int em28xx_capture_start(struct em28xx *dev, int start); +int em28xx_vbi_supported(struct em28xx *dev); int em28xx_set_outfmt(struct em28xx *dev); int em28xx_resolution_set(struct em28xx *dev); int em28xx_set_alternate(struct em28xx *dev); @@ -697,6 +714,9 @@ void em28xx_deregister_snapshot_button(struct em28xx *dev); int em28xx_ir_init(struct em28xx *dev); int em28xx_ir_fini(struct em28xx *dev); +/* Provided by em28xx-vbi.c */ +extern struct videobuf_queue_ops em28xx_vbi_qops; + /* printk macros */ #define em28xx_err(fmt, arg...) do {\ diff --git a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c index 1dd7c161f..38a56294d 100644 --- a/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c +++ b/linux/drivers/media/video/gspca/m5602/m5602_s5k4aa.c @@ -49,6 +49,12 @@ static DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2550") } }, { + .ident = "Fujitsu-Siemens Amilo Pa 2548", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Pa 2548") + } + }, { .ident = "MSI GX700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), @@ -56,6 +62,13 @@ static DMI_MATCH(DMI_BIOS_DATE, "07/26/2007") } }, { + .ident = "MSI GX700", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), + DMI_MATCH(DMI_PRODUCT_NAME, "GX700"), + DMI_MATCH(DMI_BIOS_DATE, "07/19/2007") + } + }, { .ident = "MSI GX700/GX705/EX700", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Micro-Star International"), diff --git a/linux/drivers/media/video/ivtv/ivtv-driver.c b/linux/drivers/media/video/ivtv/ivtv-driver.c index fbb8586c5..383d401b4 100644 --- a/linux/drivers/media/video/ivtv/ivtv-driver.c +++ b/linux/drivers/media/video/ivtv/ivtv-driver.c @@ -246,7 +246,7 @@ MODULE_PARM_DESC(newi2c, "\t\t\t-1 is autodetect, 0 is off, 1 is on\n" "\t\t\tDefault is autodetect"); -MODULE_PARM_DESC(ivtv_first_minor, "Set kernel number assigned to first card"); +MODULE_PARM_DESC(ivtv_first_minor, "Set device node number assigned to first card"); MODULE_AUTHOR("Kevin Thayer, Chris Kennedy, Hans Verkuil"); MODULE_DESCRIPTION("CX23415/CX23416 driver"); diff --git a/linux/drivers/media/video/ivtv/ivtv-streams.c b/linux/drivers/media/video/ivtv/ivtv-streams.c index 15da01710..67699e3f2 100644 --- a/linux/drivers/media/video/ivtv/ivtv-streams.c +++ b/linux/drivers/media/video/ivtv/ivtv-streams.c @@ -261,8 +261,8 @@ static int ivtv_reg_dev(struct ivtv *itv, int type) video_set_drvdata(s->vdev, s); /* Register device. First try the desired minor, then any free one. */ - if (video_register_device(s->vdev, vfl_type, num)) { - IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n", + if (video_register_device_no_warn(s->vdev, vfl_type, num)) { + IVTV_ERR("Couldn't register v4l2 device for %s (device node number %d)\n", s->name, num); video_device_release(s->vdev); s->vdev = NULL; diff --git a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c index 336a20ede..e4d7c13ca 100644 --- a/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c +++ b/linux/drivers/media/video/pvrusb2/pvrusb2-devattr.c @@ -298,6 +298,7 @@ static struct tda829x_config tda829x_no_probe = { static struct tda18271_config hauppauge_tda18271_dvb_config = { .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap) @@ -393,6 +394,7 @@ static struct tda18271_std_map hauppauge_tda18271_std_map = { static struct tda18271_config hauppauge_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap) diff --git a/linux/drivers/media/video/saa7134/saa7134-dvb.c b/linux/drivers/media/video/saa7134/saa7134-dvb.c index 18223dafe..dc2428f12 100644 --- a/linux/drivers/media/video/saa7134/saa7134-dvb.c +++ b/linux/drivers/media/video/saa7134/saa7134-dvb.c @@ -1007,6 +1007,7 @@ static struct tda18271_config hcw_tda18271_config = { .std_map = &hauppauge_tda18271_std_map, .gate = TDA18271_GATE_ANALOG, .config = 3, + .output_opt = TDA18271_OUTPUT_LT_OFF, }; static struct tda829x_config tda829x_no_probe = { diff --git a/linux/drivers/media/video/v4l2-dev.c b/linux/drivers/media/video/v4l2-dev.c index 9f04a8e6d..a6d761dec 100644 --- a/linux/drivers/media/video/v4l2-dev.c +++ b/linux/drivers/media/video/v4l2-dev.c @@ -86,7 +86,49 @@ static CLASS_DEVICE_ATTR(name, S_IRUGO, show_name, NULL); */ static struct video_device *video_device[VIDEO_NUM_DEVICES]; static DEFINE_MUTEX(videodev_lock); -static DECLARE_BITMAP(video_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); +static DECLARE_BITMAP(devnode_nums[VFL_TYPE_MAX], VIDEO_NUM_DEVICES); + +/* Device node utility functions */ + +/* Note: these utility functions all assume that vfl_type is in the range + [0, VFL_TYPE_MAX-1]. */ + +#ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES +/* Return the bitmap corresponding to vfl_type. */ +static inline unsigned long *devnode_bits(int vfl_type) +{ + /* Any types not assigned to fixed minor ranges must be mapped to + one single bitmap for the purposes of finding a free node number + since all those unassigned types use the same minor range. */ + int idx = (vfl_type > VFL_TYPE_VTX) ? VFL_TYPE_MAX - 1 : vfl_type; + + return devnode_nums[idx]; +} +#else +/* Return the bitmap corresponding to vfl_type. */ +static inline unsigned long *devnode_bits(int vfl_type) +{ + return devnode_nums[vfl_type]; +} +#endif + +/* Mark device node number vdev->num as used */ +static inline void devnode_set(struct video_device *vdev) +{ + set_bit(vdev->num, devnode_bits(vdev->vfl_type)); +} + +/* Mark device node number vdev->num as unused */ +static inline void devnode_clear(struct video_device *vdev) +{ + clear_bit(vdev->num, devnode_bits(vdev->vfl_type)); +} + +/* Try to find a free device node number in the range [from, to> */ +static inline int devnode_find(struct video_device *vdev, int from, int to) +{ + return find_next_zero_bit(devnode_bits(vdev->vfl_type), to, from); +} struct video_device *video_device_alloc(void) { @@ -151,8 +193,8 @@ static void v4l2_device_release(struct device *cd) the release() callback. */ vdev->cdev = NULL; - /* Mark minor as free */ - clear_bit(vdev->num, video_nums[vdev->vfl_type]); + /* Mark device node number as free */ + devnode_clear(vdev); mutex_unlock(&videodev_lock); @@ -376,13 +418,16 @@ static int get_index(struct video_device *vdev) * video_register_device - register video4linux devices * @vdev: video device structure we want to register * @type: type of device to register - * @nr: which device number (0 == /dev/video0, 1 == /dev/video1, ... + * @nr: which device node number (0 == /dev/video0, 1 == /dev/video1, ... * -1 == first free) + * @warn_if_nr_in_use: warn if the desired device node number + * was already in use and another number was chosen instead. * - * The registration code assigns minor numbers based on the type - * requested. -ENFILE is returned in all the device slots for this - * category are full. If not then the minor field is set and the - * driver initialize function is called (if non %NULL). + * The registration code assigns minor numbers and device node numbers + * based on the requested type and registers the new device node with + * the kernel. + * An error is returned if no free minor or device node number could be + * found, or if the registration of the device node failed. * * Zero is returned on success. * @@ -396,7 +441,8 @@ static int get_index(struct video_device *vdev) * * %VFL_TYPE_RADIO - A radio card */ -int video_register_device(struct video_device *vdev, int type, int nr) +static int __video_register_device(struct video_device *vdev, int type, int nr, + int warn_if_nr_in_use) { int i = 0; int ret; @@ -439,7 +485,7 @@ int video_register_device(struct video_device *vdev, int type, int nr) if (vdev->v4l2_dev && vdev->v4l2_dev->dev) vdev->parent = vdev->v4l2_dev->dev; - /* Part 2: find a free minor, kernel number and device index. */ + /* Part 2: find a free minor, device node number and device index. */ #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES /* Keep the ranges for the first four types for historical * reasons. @@ -470,21 +516,22 @@ int video_register_device(struct video_device *vdev, int type, int nr) } #endif - /* Pick a minor number */ + /* Pick a device node number */ mutex_lock(&videodev_lock); - nr = find_next_zero_bit(video_nums[type], minor_cnt, nr == -1 ? 0 : nr); + nr = devnode_find(vdev, nr == -1 ? 0 : nr, minor_cnt); if (nr == minor_cnt) - nr = find_first_zero_bit(video_nums[type], minor_cnt); + nr = devnode_find(vdev, 0, minor_cnt); if (nr == minor_cnt) { - printk(KERN_ERR "could not get a free kernel number\n"); + printk(KERN_ERR "could not get a free device node number\n"); mutex_unlock(&videodev_lock); return -ENFILE; } #ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES - /* 1-on-1 mapping of kernel number to minor number */ + /* 1-on-1 mapping of device node number to minor number */ i = nr; #else - /* The kernel number and minor numbers are independent */ + /* The device node number and minor numbers are independent, so + we just find the first free minor number. */ for (i = 0; i < VIDEO_NUM_DEVICES; i++) if (video_device[i] == NULL) break; @@ -496,7 +543,8 @@ int video_register_device(struct video_device *vdev, int type, int nr) #endif vdev->minor = i + minor_offset; vdev->num = nr; - set_bit(nr, video_nums[type]); + devnode_set(vdev); + /* Should not happen since we thought this minor was free */ WARN_ON(video_device[vdev->minor] != NULL); vdev->index = get_index(vdev); @@ -538,12 +586,12 @@ int video_register_device(struct video_device *vdev, int type, int nr) #if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) if (vdev->parent) vdev->dev.dev = vdev->parent; - sprintf(vdev->dev.class_id, "%s%d", name_base, nr); + sprintf(vdev->dev.class_id, "%s%d", name_base, vdev->num); ret = class_device_register(&vdev->dev); #else if (vdev->parent) vdev->dev.parent = vdev->parent; - dev_set_name(&vdev->dev, "%s%d", name_base, nr); + dev_set_name(&vdev->dev, "%s%d", name_base, vdev->num); ret = device_register(&vdev->dev); #endif if (ret < 0) { @@ -570,6 +618,10 @@ int video_register_device(struct video_device *vdev, int type, int nr) reference to the device goes away. */ vdev->dev.release = v4l2_device_release; + if (nr != -1 && nr != vdev->num && warn_if_nr_in_use) + printk(KERN_WARNING "%s: requested %s%d, got %s%d\n", + __func__, name_base, nr, name_base, vdev->num); + /* Part 5: Activate this minor. The char device can now be used. */ mutex_lock(&videodev_lock); video_device[vdev->minor] = vdev; @@ -580,14 +632,25 @@ cleanup: mutex_lock(&videodev_lock); if (vdev->cdev) cdev_del(vdev->cdev); - clear_bit(vdev->num, video_nums[type]); + devnode_clear(vdev); mutex_unlock(&videodev_lock); /* Mark this video device as never having been registered. */ vdev->minor = -1; return ret; } + +int video_register_device(struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 1); +} EXPORT_SYMBOL(video_register_device); +int video_register_device_no_warn(struct video_device *vdev, int type, int nr) +{ + return __video_register_device(vdev, type, nr, 0); +} +EXPORT_SYMBOL(video_register_device_no_warn); + /** * video_unregister_device - unregister a video4linux device * @vdev: the device to unregister diff --git a/linux/drivers/media/video/videobuf-dma-contig.c b/linux/drivers/media/video/videobuf-dma-contig.c index 633a124cf..bd1b13e93 100644 --- a/linux/drivers/media/video/videobuf-dma-contig.c +++ b/linux/drivers/media/video/videobuf-dma-contig.c @@ -17,6 +17,7 @@ #include <linux/init.h> #include <linux/module.h> #include <linux/mm.h> +#include <linux/pagemap.h> #include <linux/dma-mapping.h> #include <media/videobuf-dma-contig.h> #include "compat.h" @@ -26,6 +27,7 @@ struct videobuf_dma_contig_memory { void *vaddr; dma_addr_t dma_handle; unsigned long size; + int is_userptr; }; #define MAGIC_DC_MEM 0x0733ac61 @@ -109,6 +111,82 @@ static struct vm_operations_struct videobuf_vm_ops = { .close = videobuf_vm_close, }; +/** + * videobuf_dma_contig_user_put() - reset pointer to user space buffer + * @mem: per-buffer private videobuf-dma-contig data + * + * This function resets the user space pointer + */ +static void videobuf_dma_contig_user_put(struct videobuf_dma_contig_memory *mem) +{ + mem->is_userptr = 0; + mem->dma_handle = 0; + mem->size = 0; +} + +/** + * videobuf_dma_contig_user_get() - setup user space memory pointer + * @mem: per-buffer private videobuf-dma-contig data + * @vb: video buffer to map + * + * This function validates and sets up a pointer to user space memory. + * Only physically contiguous pfn-mapped memory is accepted. + * + * Returns 0 if successful. + */ +static int videobuf_dma_contig_user_get(struct videobuf_dma_contig_memory *mem, + struct videobuf_buffer *vb) +{ + struct mm_struct *mm = current->mm; + struct vm_area_struct *vma; + unsigned long prev_pfn, this_pfn; + unsigned long pages_done, user_address; + int ret; + + mem->size = PAGE_ALIGN(vb->size); + mem->is_userptr = 0; + ret = -EINVAL; + + down_read(&mm->mmap_sem); + + vma = find_vma(mm, vb->baddr); + if (!vma) + goto out_up; + + if ((vb->baddr + mem->size) > vma->vm_end) + goto out_up; + + pages_done = 0; + prev_pfn = 0; /* kill warning */ + user_address = vb->baddr; + + while (pages_done < (mem->size >> PAGE_SHIFT)) { + ret = follow_pfn(vma, user_address, &this_pfn); + if (ret) + break; + + if (pages_done == 0) + mem->dma_handle = this_pfn << PAGE_SHIFT; + else if (this_pfn != (prev_pfn + 1)) + ret = -EFAULT; + + if (ret) + break; + + prev_pfn = this_pfn; + user_address += PAGE_SIZE; + pages_done++; + } + + if (!ret) + mem->is_userptr = 1; + + out_up: + up_read(¤t->mm->mmap_sem); + + return ret; +} + static void *__videobuf_alloc(size_t size) { struct videobuf_dma_contig_memory *mem; @@ -155,12 +233,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, case V4L2_MEMORY_USERPTR: dev_dbg(q->dev, "%s memory method USERPTR\n", __func__); - /* The only USERPTR currently supported is the one needed for - read() method. - */ + /* handle pointer from user space */ if (vb->baddr) - return -EINVAL; + return videobuf_dma_contig_user_get(mem, vb); + /* allocate memory for the read() method */ mem->size = PAGE_ALIGN(vb->size); mem->vaddr = dma_alloc_coherent(q->dev, mem->size, &mem->dma_handle, GFP_KERNEL); @@ -387,7 +464,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, So, it should free memory only if the memory were allocated for read() operation. */ - if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr) + if (buf->memory != V4L2_MEMORY_USERPTR) return; if (!mem) @@ -395,6 +472,13 @@ void videobuf_dma_contig_free(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_DC_MEM); + /* handle user space pointer case */ + if (buf->baddr) { + videobuf_dma_contig_user_put(mem); + return; + } + + /* read() method */ dma_free_coherent(q->dev, mem->size, mem->vaddr, mem->dma_handle); mem->vaddr = NULL; } diff --git a/linux/drivers/staging/cx25821/Kconfig b/linux/drivers/staging/cx25821/Kconfig new file mode 100644 index 000000000..df7756a95 --- /dev/null +++ b/linux/drivers/staging/cx25821/Kconfig @@ -0,0 +1,34 @@ +config VIDEO_CX25821 + tristate "Conexant cx25821 support" + depends on DVB_CORE && VIDEO_DEV && PCI && I2C && INPUT + select I2C_ALGOBIT + select VIDEO_BTCX + select VIDEO_TVEEPROM + select VIDEO_IR + select VIDEOBUF_DVB + select VIDEOBUF_DMA_SG + select VIDEO_CX25840 + select VIDEO_CX2341X + ---help--- + This is a video4linux driver for Conexant 25821 based + TV cards. + + To compile this driver as a module, choose M here: the + module will be called cx25821 + +config VIDEO_CX25821_ALSA + tristate "Conexant 25821 DMA audio support" + depends on VIDEO_CX25821 && SND && EXPERIMENTAL + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio on + Conexant 25821 based capture cards using ALSA. + + It only works with boards with function 01 enabled. + To check if your board supports, use lspci -n. + If supported, you should see 14f1:8801 or 14f1:8811 + PCI device. + + To compile this driver as a module, choose M here: the + module will be called cx25821-alsa. + diff --git a/linux/drivers/staging/cx25821/Makefile b/linux/drivers/staging/cx25821/Makefile new file mode 100644 index 000000000..2029cd046 --- /dev/null +++ b/linux/drivers/staging/cx25821/Makefile @@ -0,0 +1,13 @@ +cx25821-objs := cx25821-core.o cx25821-cards.o cx25821-i2c.o cx25821-gpio.o \ + cx25821-medusa-video.o cx25821-video.o cx25821-video0.o cx25821-video1.o \ + cx25821-video2.o cx25821-video3.o cx25821-video4.o cx25821-video5.o \ + cx25821-video6.o cx25821-video7.o cx25821-vidups9.o cx25821-vidups10.o \ + cx25821-audups11.o cx25821-video-upstream.o cx25821-video-upstream-ch2.o \ + cx25821-audio-upstream.o cx25821-videoioctl.o + +obj-$(CONFIG_VIDEO_CX25821) += cx25821.o +obj-$(CONFIG_VIDEO_CX25821_ALSA) += cx25821-alsa.o + +EXTRA_CFLAGS += -Idrivers/media/video +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends diff --git a/linux/drivers/staging/cx25821/README b/linux/drivers/staging/cx25821/README new file mode 100644 index 000000000..a9ba50b98 --- /dev/null +++ b/linux/drivers/staging/cx25821/README @@ -0,0 +1,6 @@ +Todo: + - checkpatch.pl cleanups + - sparse cleanups + +Please send patches to linux-media@vger.kernel.org + diff --git a/linux/drivers/staging/cx25821/cx25821-alsa.c b/linux/drivers/staging/cx25821/cx25821-alsa.c new file mode 100644 index 000000000..bd532ecef --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-alsa.c @@ -0,0 +1,812 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on SAA713x ALSA driver and CX88 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/vmalloc.h> +#include <linux/dma-mapping.h> +#include <linux/pci.h> + +#include <asm/delay.h> +#include "compat.h" +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19) +#include <sound/tlv.h> +#endif + +#include "cx25821.h" +#include "cx25821-reg.h" + +#define AUDIO_SRAM_CHANNEL SRAM_CH08 + +#define dprintk(level,fmt, arg...) if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->dev->name , ## arg) + +#define dprintk_core(level,fmt, arg...) if (debug >= level) \ + printk(KERN_DEBUG "%s/1: " fmt, chip->dev->name , ## arg) + +/**************************************************************************** + Data type declarations - Can be moded to a header file later + ****************************************************************************/ + +static struct snd_card *snd_cx25821_cards[SNDRV_CARDS]; +static int devno; + +struct cx25821_audio_dev { + struct cx25821_dev *dev; + struct cx25821_dmaqueue q; +#if 0 + u64 starttime; +#endif + + /* pci i/o */ + struct pci_dev *pci; + + /* audio controls */ + int irq; + + struct snd_card *card; + + unsigned long iobase; + spinlock_t reg_lock; + atomic_t count; + + unsigned int dma_size; + unsigned int period_size; + unsigned int num_periods; + + struct videobuf_dmabuf *dma_risc; + + struct cx25821_buffer *buf; + + struct snd_pcm_substream *substream; +}; +typedef struct cx25821_audio_dev snd_cx25821_card_t; + +#ifdef COMPAT_SND_CTL_BOOLEAN_MONO +static int snd_ctl_boolean_mono_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + return 0; +} +#endif + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = { 1,[1 ... (SNDRV_CARDS - 1)] = 1 }; + +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable cx25821 soundcard. default enabled."); + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for cx25821 capture interface(s)."); + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for cx25821 based capture cards"); +MODULE_AUTHOR("Hiep Huynh"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Conexant,25821}"); //"{{Conexant,23881}," + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific funtions + ****************************************************************************/ +/* Constants taken from cx88-reg.h */ +#define AUD_INT_DN_RISCI1 (1 << 0) +#define AUD_INT_UP_RISCI1 (1 << 1) +#define AUD_INT_RDS_DN_RISCI1 (1 << 2) +#define AUD_INT_DN_RISCI2 (1 << 4) /* yes, 3 is skipped */ +#define AUD_INT_UP_RISCI2 (1 << 5) +#define AUD_INT_RDS_DN_RISCI2 (1 << 6) +#define AUD_INT_DN_SYNC (1 << 12) +#define AUD_INT_UP_SYNC (1 << 13) +#define AUD_INT_RDS_DN_SYNC (1 << 14) +#define AUD_INT_OPC_ERR (1 << 16) +#define AUD_INT_BER_IRQ (1 << 20) +#define AUD_INT_MCHG_IRQ (1 << 21) +#define GP_COUNT_CONTROL_RESET 0x3 + +#define PCI_MSK_AUD_EXT (1 << 4) +#define PCI_MSK_AUD_INT (1 << 3) +/* + * BOARD Specific: Sets audio DMA + */ + +static int _cx25821_start_audio_dma(snd_cx25821_card_t * chip) +{ + struct cx25821_buffer *buf = chip->buf; + struct cx25821_dev *dev = chip->dev; + struct sram_channel *audio_ch = + &cx25821_sram_channels[AUDIO_SRAM_CHANNEL]; + u32 tmp = 0; + + // enable output on the GPIO 0 for the MCLK ADC (Audio) + cx25821_set_gpiopin_direction(chip->dev, 0, 0); + + /* Make sure RISC/FIFO are off before changing FIFO/RISC settings */ + cx_clear(AUD_INT_DMA_CTL, + FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); + + /* setup fifo + format - out channel */ + cx25821_sram_channel_setup_audio(chip->dev, audio_ch, buf->bpl, + buf->risc.dma); + + /* sets bpl size */ + cx_write(AUD_A_LNGTH, buf->bpl); + + /* reset counter */ + cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); //GP_COUNT_CONTROL_RESET = 0x3 + atomic_set(&chip->count, 0); + + //Set the input mode to 16-bit + tmp = cx_read(AUD_A_CFG); + cx_write(AUD_A_CFG, + tmp | FLD_AUD_DST_PK_MODE | FLD_AUD_DST_ENABLE | + FLD_AUD_CLK_ENABLE); + + //printk(KERN_INFO "DEBUG: Start audio DMA, %d B/line, cmds_start(0x%x)= %d lines/FIFO, %d periods, %d " + // "byte buffer\n", buf->bpl, audio_ch->cmds_start, cx_read(audio_ch->cmds_start + 12)>>1, + // chip->num_periods, buf->bpl * chip->num_periods); + + /* Enables corresponding bits at AUD_INT_STAT */ + cx_write(AUD_A_INT_MSK, + FLD_AUD_DST_RISCI1 | FLD_AUD_DST_OF | FLD_AUD_DST_SYNC | + FLD_AUD_DST_OPC_ERR); + + /* Clean any pending interrupt bits already set */ + cx_write(AUD_A_INT_STAT, ~0); + + /* enable audio irqs */ + cx_set(PCI_INT_MSK, chip->dev->pci_irqmask | PCI_MSK_AUD_INT); + + // Turn on audio downstream fifo and risc enable 0x101 + tmp = cx_read(AUD_INT_DMA_CTL); + cx_set(AUD_INT_DMA_CTL, + tmp | (FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN)); + + mdelay(100); + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _cx25821_stop_audio_dma(snd_cx25821_card_t * chip) +{ + struct cx25821_dev *dev = chip->dev; + + /* stop dma */ + cx_clear(AUD_INT_DMA_CTL, + FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); + + /* disable irqs */ + cx_clear(PCI_INT_MSK, PCI_MSK_AUD_INT); + cx_clear(AUD_A_INT_MSK, + AUD_INT_OPC_ERR | AUD_INT_DN_SYNC | AUD_INT_DN_RISCI2 | + AUD_INT_DN_RISCI1); + + return 0; +} + +#define MAX_IRQ_LOOP 50 + +/* + * BOARD Specific: IRQ dma bits + */ +static char *cx25821_aud_irqs[32] = { + "dn_risci1", "up_risci1", "rds_dn_risc1", /* 0-2 */ + NULL, /* reserved */ + "dn_risci2", "up_risci2", "rds_dn_risc2", /* 4-6 */ + NULL, /* reserved */ + "dnf_of", "upf_uf", "rds_dnf_uf", /* 8-10 */ + NULL, /* reserved */ + "dn_sync", "up_sync", "rds_dn_sync", /* 12-14 */ + NULL, /* reserved */ + "opc_err", "par_err", "rip_err", /* 16-18 */ + "pci_abort", "ber_irq", "mchg_irq" /* 19-21 */ +}; + +/* + * BOARD Specific: Threats IRQ audio specific calls + */ +static void cx25821_aud_irq(snd_cx25821_card_t * chip, u32 status, u32 mask) +{ + struct cx25821_dev *dev = chip->dev; + + if (0 == (status & mask)) { + return; + } + + cx_write(AUD_A_INT_STAT, status); + if (debug > 1 || (status & mask & ~0xff)) + cx25821_print_irqbits(dev->name, "irq aud", + cx25821_aud_irqs, + ARRAY_SIZE(cx25821_aud_irqs), status, + mask); + + /* risc op code error */ + if (status & AUD_INT_OPC_ERR) { + printk(KERN_WARNING "WARNING %s/1: Audio risc op code error\n", + dev->name); + + cx_clear(AUD_INT_DMA_CTL, + FLD_AUD_DST_A_RISC_EN | FLD_AUD_DST_A_FIFO_EN); + cx25821_sram_channel_dump_audio(dev, + &cx25821_sram_channels + [AUDIO_SRAM_CHANNEL]); + } + if (status & AUD_INT_DN_SYNC) { + printk(KERN_WARNING "WARNING %s: Downstream sync error!\n", + dev->name); + cx_write(AUD_A_GPCNT_CTL, GP_COUNT_CONTROL_RESET); + return; + } + + /* risc1 downstream */ + if (status & AUD_INT_DN_RISCI1) { + atomic_set(&chip->count, cx_read(AUD_A_GPCNT)); + snd_pcm_period_elapsed(chip->substream); + } +} + +/* + * BOARD Specific: Handles IRQ calls + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static irqreturn_t cx25821_irq(int irq, void *dev_id, struct pt_regs *regs) +#else +static irqreturn_t cx25821_irq(int irq, void *dev_id) +#endif +{ + snd_cx25821_card_t *chip = dev_id; + struct cx25821_dev *dev = chip->dev; + u32 status, pci_status; + u32 audint_status, audint_mask; + int loop, handled = 0; + int audint_count = 0; + + audint_status = cx_read(AUD_A_INT_STAT); + audint_mask = cx_read(AUD_A_INT_MSK); + audint_count = cx_read(AUD_A_GPCNT); + status = cx_read(PCI_INT_STAT); + + for (loop = 0; loop < 1; loop++) { + status = cx_read(PCI_INT_STAT); + if (0 == status) { + status = cx_read(PCI_INT_STAT); + audint_status = cx_read(AUD_A_INT_STAT); + audint_mask = cx_read(AUD_A_INT_MSK); + + if (status) { + handled = 1; + cx_write(PCI_INT_STAT, status); + + cx25821_aud_irq(chip, audint_status, + audint_mask); + break; + } else + goto out; + } + + handled = 1; + cx_write(PCI_INT_STAT, status); + + cx25821_aud_irq(chip, audint_status, audint_mask); + } + + pci_status = cx_read(PCI_INT_STAT); + + if (handled) + cx_write(PCI_INT_STAT, pci_status); + + out: + return IRQ_RETVAL(handled); +} + +static int dsp_buffer_free(snd_cx25821_card_t * chip) +{ + BUG_ON(!chip->dma_size); + + dprintk(2, "Freeing buffer\n"); + videobuf_sg_dma_unmap(&chip->pci->dev, chip->dma_risc); + videobuf_dma_free(chip->dma_risc); + btcx_riscmem_free(chip->pci, &chip->buf->risc); + kfree(chip->buf); + + chip->dma_risc = NULL; + chip->dma_size = 0; + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#if 1 +#define DEFAULT_FIFO_SIZE 384 +static struct snd_pcm_hardware snd_cx25821_digital_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, + /* Analog audio output will be full of clicks and pops if there + are not exactly four lines in the SRAM FIFO buffer. */ + .period_bytes_min = DEFAULT_FIFO_SIZE / 3, + .period_bytes_max = DEFAULT_FIFO_SIZE / 3, + .periods_min = 1, + .periods_max = AUDIO_LINE_SIZE, + .buffer_bytes_max = (AUDIO_LINE_SIZE * AUDIO_LINE_SIZE), //128*128 = 16384 = 1024 * 16 +}; +#endif + +/* + * audio pcm capture open callback + */ +static int snd_cx25821_pcm_open(struct snd_pcm_substream *substream) +{ + snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + unsigned int bpl = 0; + + if (!chip) { + printk(KERN_ERR "DEBUG: cx25821 can't find device struct." + " Can't proceed with open\n"); + return -ENODEV; + } + + err = + snd_pcm_hw_constraint_pow2(runtime, 0, SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_cx25821_digital_hw; + + if (cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size != + DEFAULT_FIFO_SIZE) { + bpl = cx25821_sram_channels[AUDIO_SRAM_CHANNEL].fifo_size / 3; //since there are 3 audio Clusters + bpl &= ~7; /* must be multiple of 8 */ + + if (bpl > AUDIO_LINE_SIZE) { + bpl = AUDIO_LINE_SIZE; + } + runtime->hw.period_bytes_min = bpl; + runtime->hw.period_bytes_max = bpl; + } + + return 0; + _error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_cx25821_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * hw_params callback + */ +static int snd_cx25821_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct videobuf_dmabuf *dma; + + struct cx25821_buffer *buf; + int ret; + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + chip->period_size = params_period_bytes(hw_params); + chip->num_periods = params_periods(hw_params); + chip->dma_size = chip->period_size * params_periods(hw_params); + + BUG_ON(!chip->dma_size); + BUG_ON(chip->num_periods & (chip->num_periods - 1)); + + buf = videobuf_sg_alloc(sizeof(*buf)); + if (NULL == buf) + return -ENOMEM; + + if (chip->period_size > AUDIO_LINE_SIZE) { + chip->period_size = AUDIO_LINE_SIZE; + } + + buf->vb.memory = V4L2_MEMORY_MMAP; + buf->vb.field = V4L2_FIELD_NONE; + buf->vb.width = chip->period_size; + buf->bpl = chip->period_size; + buf->vb.height = chip->num_periods; + buf->vb.size = chip->dma_size; + + dma = videobuf_to_dma(&buf->vb); + videobuf_dma_init(dma); + + ret = videobuf_dma_init_kernel(dma, PCI_DMA_FROMDEVICE, + (PAGE_ALIGN(buf->vb.size) >> + PAGE_SHIFT)); + if (ret < 0) + goto error; + + ret = videobuf_sg_dma_map(&chip->pci->dev, dma); + if (ret < 0) + goto error; + + ret = + cx25821_risc_databuffer_audio(chip->pci, &buf->risc, dma->sglist, + buf->vb.width, buf->vb.height, 1); + if (ret < 0) { + printk(KERN_INFO + "DEBUG: ERROR after cx25821_risc_databuffer_audio() \n"); + goto error; + } + + /* Loop back to start of program */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + buf->vb.state = VIDEOBUF_PREPARED; + + chip->buf = buf; + chip->dma_risc = dma; + + substream->runtime->dma_area = chip->dma_risc->vmalloc; + substream->runtime->dma_bytes = chip->dma_size; + substream->runtime->dma_addr = 0; + + return 0; + + error: + kfree(buf); + return ret; +} + +/* + * hw free callback + */ +static int snd_cx25821_hw_free(struct snd_pcm_substream *substream) +{ + snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +/* + * prepare callback + */ +static int snd_cx25821_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * trigger callback + */ +static int snd_cx25821_card_trigger(struct snd_pcm_substream *substream, + int cmd) +{ + snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + int err = 0; + + /* Local interrupts are already disabled by ALSA */ + spin_lock(&chip->reg_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = _cx25821_start_audio_dma(chip); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = _cx25821_stop_audio_dma(chip); + break; + default: + err = -EINVAL; + break; + } + + spin_unlock(&chip->reg_lock); + + return err; +} + +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_cx25821_pointer(struct snd_pcm_substream + *substream) +{ + snd_cx25821_card_t *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + u16 count; + + count = atomic_read(&chip->count); + + return runtime->period_size * (count & (runtime->periods - 1)); +} + +/* + * page callback (needed for mmap) + */ +static struct page *snd_cx25821_page(struct snd_pcm_substream *substream, + unsigned long offset) +{ + void *pageptr = substream->runtime->dma_area + offset; + + return vmalloc_to_page(pageptr); +} + +/* + * operators + */ +static struct snd_pcm_ops snd_cx25821_pcm_ops = { + .open = snd_cx25821_pcm_open, + .close = snd_cx25821_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_cx25821_hw_params, + .hw_free = snd_cx25821_hw_free, + .prepare = snd_cx25821_prepare, + .trigger = snd_cx25821_card_trigger, + .pointer = snd_cx25821_pointer, + .page = snd_cx25821_page, +}; + +/* + * ALSA create a PCM device: Called when initializing the board. Sets up the name and hooks up + * the callbacks + */ +static int snd_cx25821_pcm(snd_cx25821_card_t * chip, int device, char *name) +{ + struct snd_pcm *pcm; + int err; + + err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); + if (err < 0) { + printk(KERN_INFO "ERROR: FAILED snd_pcm_new() in %s\n", + __func__); + return err; + } + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_cx25821_pcm_ops); + + return 0; +} + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * PCI ID Table - 14f1:8801 and 14f1:8811 means function 1: Audio + * Only boards with eeprom and byte 1 at eeprom=1 have it + */ + +static struct pci_device_id cx25821_audio_pci_tbl[] __devinitdata = { + {0x14f1, 0x0920, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, + {0,} +}; + +MODULE_DEVICE_TABLE(pci, cx25821_audio_pci_tbl); + +/* + * Not used in the function snd_cx25821_dev_free so removing + * from the file. + */ +/* +static int snd_cx25821_free(snd_cx25821_card_t *chip) +{ + if (chip->irq >= 0) + free_irq(chip->irq, chip); + + cx25821_dev_unregister(chip->dev); + pci_disable_device(chip->pci); + + return 0; +} +*/ + +/* + * Component Destructor + */ +static void snd_cx25821_dev_free(struct snd_card *card) +{ + snd_cx25821_card_t *chip = card->private_data; + + //snd_cx25821_free(chip); + snd_card_free(chip->card); +} + +/* + * Alsa Constructor - Component probe + */ +static int cx25821_audio_initdev(struct cx25821_dev *dev) +{ + struct snd_card *card; + snd_cx25821_card_t *chip; + int err; + + if (devno >= SNDRV_CARDS) { + printk(KERN_INFO "DEBUG ERROR: devno >= SNDRV_CARDS %s\n", + __func__); + return (-ENODEV); + } + + if (!enable[devno]) { + ++devno; + printk(KERN_INFO "DEBUG ERROR: !enable[devno] %s\n", __func__); + return (-ENOENT); + } + + err = snd_card_create(index[devno], id[devno], THIS_MODULE, + sizeof(snd_cx25821_card_t), &card); + if (err < 0) { + printk(KERN_INFO + "DEBUG ERROR: cannot create snd_card_new in %s\n", + __func__); + return err; + } + + strcpy(card->driver, "cx25821"); + + /* Card "creation" */ + card->private_free = snd_cx25821_dev_free; + chip = (snd_cx25821_card_t *) card->private_data; + spin_lock_init(&chip->reg_lock); + + chip->dev = dev; + chip->card = card; + chip->pci = dev->pci; + chip->iobase = pci_resource_start(dev->pci, 0); + + chip->irq = dev->pci->irq; + + err = request_irq(dev->pci->irq, cx25821_irq, + IRQF_SHARED | IRQF_DISABLED, chip->dev->name, chip); + + if (err < 0) { + printk(KERN_ERR "ERROR %s: can't get IRQ %d for ALSA\n", + chip->dev->name, dev->pci->irq); + goto error; + } + + if ((err = snd_cx25821_pcm(chip, 0, "cx25821 Digital")) < 0) { + printk(KERN_INFO + "DEBUG ERROR: cannot create snd_cx25821_pcm %s\n", + __func__); + goto error; + } + + snd_card_set_dev(card, &chip->pci->dev); + + strcpy(card->shortname, "cx25821"); + sprintf(card->longname, "%s at 0x%lx irq %d", chip->dev->name, + chip->iobase, chip->irq); + strcpy(card->mixername, "CX25821"); + + printk(KERN_INFO "%s/%i: ALSA support for cx25821 boards\n", + card->driver, devno); + + err = snd_card_register(card); + if (err < 0) { + printk(KERN_INFO "DEBUG ERROR: cannot register sound card %s\n", + __func__); + goto error; + } + + snd_cx25821_cards[devno] = card; + + devno++; + return 0; + + error: + snd_card_free(card); + return err; +} + +/**************************************************************************** + LINUX MODULE INIT + ****************************************************************************/ +static void cx25821_audio_fini(void) +{ + snd_card_free(snd_cx25821_cards[0]); +} + +/* + * Module initializer + * + * Loops through present saa7134 cards, and assigns an ALSA device + * to each one + * + */ +static int cx25821_alsa_init(void) +{ + struct cx25821_dev *dev = NULL; + struct list_head *list; + + list_for_each(list, &cx25821_devlist) { + dev = list_entry(list, struct cx25821_dev, devlist); + cx25821_audio_initdev(dev); + } + + if (dev == NULL) + printk(KERN_INFO + "cx25821 ERROR ALSA: no cx25821 cards found\n"); + + return 0; + +} + +late_initcall(cx25821_alsa_init); +module_exit(cx25821_audio_fini); + +/* ----------------------------------------------------------- */ +/* + * Local variables: + * c-basic-offset: 8 + * End: + */ diff --git a/linux/drivers/staging/cx25821/cx25821-audio-upstream.c b/linux/drivers/staging/cx25821/cx25821-audio-upstream.c new file mode 100644 index 000000000..ddddf6512 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-audio-upstream.c @@ -0,0 +1,804 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" +#include "cx25821-audio-upstream.h" + +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <linux/delay.h> +#include <asm/uaccess.h> + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); +MODULE_LICENSE("GPL"); + +static int _intr_msk = + FLD_AUD_SRC_RISCI1 | FLD_AUD_SRC_OF | FLD_AUD_SRC_SYNC | + FLD_AUD_SRC_OPC_ERR; + +int cx25821_sram_channel_setup_upstream_audio(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 3) { + lines = 3; + } + + BUG_ON(lines < 2); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + /* write CMDS */ + cx_write(ch->cmds_start + 0, risc); + + cx_write(ch->cmds_start + 4, 0); + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, AUDIO_CDT_SIZE_QW); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + //IQ size + cx_write(ch->cmds_start + 20, AUDIO_IQ_SIZE_DW); + + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, AUDIO_CDT_SIZE_QW); + cx_write(ch->cnt1_reg, AUDIO_CLUSTER_SIZE_QW - 1); + + return 0; +} + +static __le32 *cx25821_risc_field_upstream_audio(struct cx25821_dev *dev, + __le32 * rp, + dma_addr_t databuf_phys_addr, + unsigned int bpl, + int fifo_enable) +{ + unsigned int line; + struct sram_channel *sram_ch = + &dev->sram_channels[dev->_audio_upstream_channel_select]; + int offset = 0; + + /* scan lines */ + for (line = 0; line < LINES_PER_AUDIO_BUFFER; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(databuf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + // Check if we need to enable the FIFO after the first 3 lines + // For the upstream audio channel, the risc engine will enable the FIFO. + if (fifo_enable && line == 2) { + *(rp++) = RISC_WRITECR; + *(rp++) = sram_ch->dma_ctl; + *(rp++) = sram_ch->fld_aud_fifo_en; + *(rp++) = 0x00000020; + } + + offset += AUDIO_LINE_SIZE; + } + + return rp; +} + +int cx25821_risc_buffer_upstream_audio(struct cx25821_dev *dev, + struct pci_dev *pci, + unsigned int bpl, unsigned int lines) +{ + __le32 *rp; + int fifo_enable = 0; + int frame = 0, i = 0; + int frame_size = AUDIO_DATA_BUF_SZ; + int databuf_offset = 0; + int risc_flag = RISC_CNT_INC; + dma_addr_t risc_phys_jump_addr; + + /* Virtual address of Risc buffer program */ + rp = dev->_risc_virt_addr; + + /* sync instruction */ + *(rp++) = cpu_to_le32(RISC_RESYNC | AUDIO_SYNC_LINE); + + for (frame = 0; frame < NUM_AUDIO_FRAMES; frame++) { + databuf_offset = frame_size * frame; + + if (frame == 0) { + fifo_enable = 1; + risc_flag = RISC_CNT_RESET; + } else { + fifo_enable = 0; + risc_flag = RISC_CNT_INC; + } + + //Calculate physical jump address + if ((frame + 1) == NUM_AUDIO_FRAMES) { + risc_phys_jump_addr = + dev->_risc_phys_start_addr + + RISC_SYNC_INSTRUCTION_SIZE; + } else { + risc_phys_jump_addr = + dev->_risc_phys_start_addr + + RISC_SYNC_INSTRUCTION_SIZE + + AUDIO_RISC_DMA_BUF_SIZE * (frame + 1); + } + + rp = cx25821_risc_field_upstream_audio(dev, rp, + dev-> + _audiodata_buf_phys_addr + + databuf_offset, bpl, + fifo_enable); + + if (USE_RISC_NOOP_AUDIO) { + for (i = 0; i < NUM_NO_OPS; i++) { + *(rp++) = cpu_to_le32(RISC_NOOP); + } + } + + // Loop to (Nth)FrameRISC or to Start of Risc program & generate IRQ + *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + + //Recalculate virtual address based on frame index + rp = dev->_risc_virt_addr + RISC_SYNC_INSTRUCTION_SIZE / 4 + + (AUDIO_RISC_DMA_BUF_SIZE * (frame + 1) / 4); + } + + return 0; +} + +void cx25821_free_memory_audio(struct cx25821_dev *dev) +{ + if (dev->_risc_virt_addr) { + pci_free_consistent(dev->pci, dev->_audiorisc_size, + dev->_risc_virt_addr, dev->_risc_phys_addr); + dev->_risc_virt_addr = NULL; + } + + if (dev->_audiodata_buf_virt_addr) { + pci_free_consistent(dev->pci, dev->_audiodata_buf_size, + dev->_audiodata_buf_virt_addr, + dev->_audiodata_buf_phys_addr); + dev->_audiodata_buf_virt_addr = NULL; + } +} + +void cx25821_stop_upstream_audio(struct cx25821_dev *dev) +{ + struct sram_channel *sram_ch = + &dev->sram_channels[AUDIO_UPSTREAM_SRAM_CHANNEL_B]; + u32 tmp = 0; + + if (!dev->_audio_is_running) { + printk + ("cx25821: No audio file is currently running so return!\n"); + return; + } + //Disable RISC interrupts + cx_write(sram_ch->int_msk, 0); + + //Turn OFF risc and fifo enable in AUD_DMA_CNTRL + tmp = cx_read(sram_ch->dma_ctl); + cx_write(sram_ch->dma_ctl, + tmp & ~(sram_ch->fld_aud_fifo_en | sram_ch->fld_aud_risc_en)); + + //Clear data buffer memory + if (dev->_audiodata_buf_virt_addr) + memset(dev->_audiodata_buf_virt_addr, 0, + dev->_audiodata_buf_size); + + dev->_audio_is_running = 0; + dev->_is_first_audio_frame = 0; + dev->_audioframe_count = 0; + dev->_audiofile_status = END_OF_FILE; + + if (dev->_irq_audio_queues) { + kfree(dev->_irq_audio_queues); + dev->_irq_audio_queues = NULL; + } + + if (dev->_audiofilename != NULL) + kfree(dev->_audiofilename); +} + +void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev) +{ + if (dev->_audio_is_running) { + cx25821_stop_upstream_audio(dev); + } + + cx25821_free_memory_audio(dev); +} + +int cx25821_get_audio_data(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + struct file *myfile; + int frame_index_temp = dev->_audioframe_index; + int i = 0; + int line_size = AUDIO_LINE_SIZE; + int frame_size = AUDIO_DATA_BUF_SZ; + int frame_offset = frame_size * frame_index_temp; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t file_offset = dev->_audioframe_count * frame_size; + loff_t pos; + mm_segment_t old_fs; + + if (dev->_audiofile_status == END_OF_FILE) + return 0; + + myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + printk("%s(): ERROR opening file(%s) with errno = %d! \n", + __func__, dev->_audiofilename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + printk("%s: File has no file operations registered!\n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + printk("%s: File has no READ operations registered! \n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (i = 0; i < dev->_audio_lines_count; i++) { + pos = file_offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 && vfs_read_retval == line_size + && dev->_audiodata_buf_virt_addr != NULL) { + memcpy((void *)(dev->_audiodata_buf_virt_addr + + frame_offset / 4), mybuf, + vfs_read_retval); + } + + file_offset += vfs_read_retval; + frame_offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + printk(KERN_INFO + "Done: exit %s() since no more bytes to read from Audio file.\n", + __func__); + break; + } + } + + if (i > 0) + dev->_audioframe_count++; + + dev->_audiofile_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + filp_close(myfile, NULL); + } + + return 0; +} + +static void cx25821_audioups_handler(struct work_struct *work) +{ + struct cx25821_dev *dev = + container_of(work, struct cx25821_dev, _audio_work_entry); + + if (!dev) { + printk("ERROR %s(): since container_of(work_struct) FAILED! \n", + __func__); + return; + } + + cx25821_get_audio_data(dev, + &dev->sram_channels[dev-> + _audio_upstream_channel_select]); +} + +int cx25821_openfile_audio(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + struct file *myfile; + int i = 0, j = 0; + int line_size = AUDIO_LINE_SIZE; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t pos; + loff_t offset = (unsigned long)0; + mm_segment_t old_fs; + + myfile = filp_open(dev->_audiofilename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + printk("%s(): ERROR opening file(%s) with errno = %d! \n", + __func__, dev->_audiofilename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + printk("%s: File has no file operations registered! \n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + printk("%s: File has no READ operations registered! \n", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (j = 0; j < NUM_AUDIO_FRAMES; j++) { + for (i = 0; i < dev->_audio_lines_count; i++) { + pos = offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 + && vfs_read_retval == line_size + && dev->_audiodata_buf_virt_addr != NULL) { + memcpy((void *)(dev-> + _audiodata_buf_virt_addr + + offset / 4), mybuf, + vfs_read_retval); + } + + offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + printk(KERN_INFO + "Done: exit %s() since no more bytes to read from Audio file.\n", + __func__); + break; + } + } + + if (i > 0) { + dev->_audioframe_count++; + } + + if (vfs_read_retval < line_size) { + break; + } + } + + dev->_audiofile_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + myfile->f_pos = 0; + filp_close(myfile, NULL); + } + + return 0; +} + +static int cx25821_audio_upstream_buffer_prepare(struct cx25821_dev *dev, + struct sram_channel *sram_ch, + int bpl) +{ + int ret = 0; + dma_addr_t dma_addr; + dma_addr_t data_dma_addr; + + cx25821_free_memory_audio(dev); + + dev->_risc_virt_addr = + pci_alloc_consistent(dev->pci, dev->audio_upstream_riscbuf_size, + &dma_addr); + dev->_risc_virt_start_addr = dev->_risc_virt_addr; + dev->_risc_phys_start_addr = dma_addr; + dev->_risc_phys_addr = dma_addr; + dev->_audiorisc_size = dev->audio_upstream_riscbuf_size; + + if (!dev->_risc_virt_addr) { + printk + ("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for RISC program! Returning.\n"); + return -ENOMEM; + } + //Clear out memory at address + memset(dev->_risc_virt_addr, 0, dev->_audiorisc_size); + + //For Audio Data buffer allocation + dev->_audiodata_buf_virt_addr = + pci_alloc_consistent(dev->pci, dev->audio_upstream_databuf_size, + &data_dma_addr); + dev->_audiodata_buf_phys_addr = data_dma_addr; + dev->_audiodata_buf_size = dev->audio_upstream_databuf_size; + + if (!dev->_audiodata_buf_virt_addr) { + printk + ("cx25821 ERROR: pci_alloc_consistent() FAILED to allocate memory for data buffer! Returning. \n"); + return -ENOMEM; + } + //Clear out memory at address + memset(dev->_audiodata_buf_virt_addr, 0, dev->_audiodata_buf_size); + + ret = cx25821_openfile_audio(dev, sram_ch); + if (ret < 0) + return ret; + + //Creating RISC programs + ret = + cx25821_risc_buffer_upstream_audio(dev, dev->pci, bpl, + dev->_audio_lines_count); + if (ret < 0) { + printk(KERN_DEBUG + "cx25821 ERROR creating audio upstream RISC programs! \n"); + goto error; + } + + return 0; + + error: + return ret; +} + +int cx25821_audio_upstream_irq(struct cx25821_dev *dev, int chan_num, + u32 status) +{ + int i = 0; + u32 int_msk_tmp; + struct sram_channel *channel = &dev->sram_channels[chan_num]; + dma_addr_t risc_phys_jump_addr; + __le32 *rp; + + if (status & FLD_AUD_SRC_RISCI1) { + //Get interrupt_index of the program that interrupted + u32 prog_cnt = cx_read(channel->gpcnt); + + //Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers + cx_write(channel->int_msk, 0); + cx_write(channel->int_stat, cx_read(channel->int_stat)); + + spin_lock(&dev->slock); + + while (prog_cnt != dev->_last_index_irq) { + //Update _last_index_irq + if (dev->_last_index_irq < (NUMBER_OF_PROGRAMS - 1)) { + dev->_last_index_irq++; + } else { + dev->_last_index_irq = 0; + } + + dev->_audioframe_index = dev->_last_index_irq; + + queue_work(dev->_irq_audio_queues, + &dev->_audio_work_entry); + } + + if (dev->_is_first_audio_frame) { + dev->_is_first_audio_frame = 0; + + if (dev->_risc_virt_start_addr != NULL) { + risc_phys_jump_addr = + dev->_risc_phys_start_addr + + RISC_SYNC_INSTRUCTION_SIZE + + AUDIO_RISC_DMA_BUF_SIZE; + + rp = cx25821_risc_field_upstream_audio(dev, + dev-> + _risc_virt_start_addr + + 1, + dev-> + _audiodata_buf_phys_addr, + AUDIO_LINE_SIZE, + FIFO_DISABLE); + + if (USE_RISC_NOOP_AUDIO) { + for (i = 0; i < NUM_NO_OPS; i++) { + *(rp++) = + cpu_to_le32(RISC_NOOP); + } + } + // Jump to 2nd Audio Frame + *(rp++) = + cpu_to_le32(RISC_JUMP | RISC_IRQ1 | + RISC_CNT_RESET); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + } + + spin_unlock(&dev->slock); + } else { + if (status & FLD_AUD_SRC_OF) + printk("%s: Audio Received Overflow Error Interrupt!\n", + __func__); + + if (status & FLD_AUD_SRC_SYNC) + printk("%s: Audio Received Sync Error Interrupt!\n", + __func__); + + if (status & FLD_AUD_SRC_OPC_ERR) + printk("%s: Audio Received OpCode Error Interrupt!\n", + __func__); + + // Read and write back the interrupt status register to clear our bits + cx_write(channel->int_stat, cx_read(channel->int_stat)); + } + + if (dev->_audiofile_status == END_OF_FILE) { + printk("cx25821: EOF Channel Audio Framecount = %d\n", + dev->_audioframe_count); + return -1; + } + //ElSE, set the interrupt mask register, re-enable irq. + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); + + return 0; +} + +static irqreturn_t cx25821_upstream_irq_audio(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 msk_stat, audio_status; + int handled = 0; + struct sram_channel *sram_ch; + + if (!dev) + return -1; + + sram_ch = &dev->sram_channels[dev->_audio_upstream_channel_select]; + + msk_stat = cx_read(sram_ch->int_mstat); + audio_status = cx_read(sram_ch->int_stat); + + // Only deal with our interrupt + if (audio_status) { + handled = + cx25821_audio_upstream_irq(dev, + dev-> + _audio_upstream_channel_select, + audio_status); + } + + if (handled < 0) { + cx25821_stop_upstream_audio(dev); + } else { + handled += handled; + } + + return IRQ_RETVAL(handled); +} + +static void cx25821_wait_fifo_enable(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + int count = 0; + u32 tmp; + + do { + //Wait 10 microsecond before checking to see if the FIFO is turned ON. + udelay(10); + + tmp = cx_read(sram_ch->dma_ctl); + + if (count++ > 1000) //10 millisecond timeout + { + printk + ("cx25821 ERROR: %s() fifo is NOT turned on. Timeout!\n", + __func__); + return; + } + + } while (!(tmp & sram_ch->fld_aud_fifo_en)); + +} + +int cx25821_start_audio_dma_upstream(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + u32 tmp = 0; + int err = 0; + + // Set the physical start address of the RISC program in the initial program counter(IPC) member of the CMDS. + cx_write(sram_ch->cmds_start + 0, dev->_risc_phys_addr); + cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ + + /* reset counter */ + cx_write(sram_ch->gpcnt_ctl, 3); + + //Set the line length (It looks like we do not need to set the line length) + cx_write(sram_ch->aud_length, AUDIO_LINE_SIZE & FLD_AUD_DST_LN_LNGTH); + + //Set the input mode to 16-bit + tmp = cx_read(sram_ch->aud_cfg); + tmp |= + FLD_AUD_SRC_ENABLE | FLD_AUD_DST_PK_MODE | FLD_AUD_CLK_ENABLE | + FLD_AUD_MASTER_MODE | FLD_AUD_CLK_SELECT_PLL_D | FLD_AUD_SONY_MODE; + cx_write(sram_ch->aud_cfg, tmp); + + // Read and write back the interrupt status register to clear it + tmp = cx_read(sram_ch->int_stat); + cx_write(sram_ch->int_stat, tmp); + + // Clear our bits from the interrupt status register. + cx_write(sram_ch->int_stat, _intr_msk); + + //Set the interrupt mask register, enable irq. + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp |= _intr_msk); + + err = + request_irq(dev->pci->irq, cx25821_upstream_irq_audio, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (err < 0) { + printk(KERN_ERR "%s: can't get upstream IRQ %d\n", dev->name, + dev->pci->irq); + goto fail_irq; + } + + // Start the DMA engine + tmp = cx_read(sram_ch->dma_ctl); + cx_set(sram_ch->dma_ctl, tmp | sram_ch->fld_aud_risc_en); + + dev->_audio_is_running = 1; + dev->_is_first_audio_frame = 1; + + // The fifo_en bit turns on by the first Risc program + cx25821_wait_fifo_enable(dev, sram_ch); + + return 0; + + fail_irq: + cx25821_dev_unregister(dev); + return err; +} + +int cx25821_audio_upstream_init(struct cx25821_dev *dev, int channel_select) +{ + struct sram_channel *sram_ch; + int retval = 0; + int err = 0; + int str_length = 0; + + if (dev->_audio_is_running) { + printk("Audio Channel is still running so return!\n"); + return 0; + } + + dev->_audio_upstream_channel_select = channel_select; + sram_ch = &dev->sram_channels[channel_select]; + + //Work queue + INIT_WORK(&dev->_audio_work_entry, cx25821_audioups_handler); + dev->_irq_audio_queues = + create_singlethread_workqueue("cx25821_audioworkqueue"); + + if (!dev->_irq_audio_queues) { + printk + ("cx25821 ERROR: create_singlethread_workqueue() for Audio FAILED!\n"); + return -ENOMEM; + } + + dev->_last_index_irq = 0; + dev->_audio_is_running = 0; + dev->_audioframe_count = 0; + dev->_audiofile_status = RESET_STATUS; + dev->_audio_lines_count = LINES_PER_AUDIO_BUFFER; + _line_size = AUDIO_LINE_SIZE; + + if (dev->input_audiofilename) { + str_length = strlen(dev->input_audiofilename); + dev->_audiofilename = + (char *)kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_audiofilename) + goto error; + + memcpy(dev->_audiofilename, dev->input_audiofilename, + str_length + 1); + + //Default if filename is empty string + if (strcmp(dev->input_audiofilename, "") == 0) { + dev->_audiofilename = "/root/audioGOOD.wav"; + } + } else { + str_length = strlen(_defaultAudioName); + dev->_audiofilename = + (char *)kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_audiofilename) + goto error; + + memcpy(dev->_audiofilename, _defaultAudioName, str_length + 1); + } + + retval = + cx25821_sram_channel_setup_upstream_audio(dev, sram_ch, _line_size, + 0); + + dev->audio_upstream_riscbuf_size = + AUDIO_RISC_DMA_BUF_SIZE * NUM_AUDIO_PROGS + + RISC_SYNC_INSTRUCTION_SIZE; + dev->audio_upstream_databuf_size = AUDIO_DATA_BUF_SZ * NUM_AUDIO_PROGS; + + //Allocating buffers and prepare RISC program + retval = + cx25821_audio_upstream_buffer_prepare(dev, sram_ch, _line_size); + if (retval < 0) { + printk(KERN_ERR + "%s: Failed to set up Audio upstream buffers!\n", + dev->name); + goto error; + } + //Start RISC engine + cx25821_start_audio_dma_upstream(dev, sram_ch); + + return 0; + + error: + cx25821_dev_unregister(dev); + + return err; +} diff --git a/linux/drivers/staging/cx25821/cx25821-audio-upstream.h b/linux/drivers/staging/cx25821/cx25821-audio-upstream.h new file mode 100644 index 000000000..db1ce7bf4 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-audio-upstream.h @@ -0,0 +1,58 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "compat.h" +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#define NUM_AUDIO_PROGS 8 +#define NUM_AUDIO_FRAMES 8 +#define END_OF_FILE 0 +#define IN_PROGRESS 1 +#define RESET_STATUS -1 +#define FIFO_DISABLE 0 +#define FIFO_ENABLE 1 +#define NUM_NO_OPS 4 + +#define RISC_READ_INSTRUCTION_SIZE 12 +#define RISC_JUMP_INSTRUCTION_SIZE 12 +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define DWORD_SIZE 4 +#define AUDIO_SYNC_LINE 4 + +#define LINES_PER_AUDIO_BUFFER 15 +#define AUDIO_LINE_SIZE 128 +#define AUDIO_DATA_BUF_SZ (AUDIO_LINE_SIZE * LINES_PER_AUDIO_BUFFER) + +#define USE_RISC_NOOP_AUDIO 1 + +#ifdef USE_RISC_NOOP_AUDIO +#define AUDIO_RISC_DMA_BUF_SIZE ( LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE + RISC_JUMP_INSTRUCTION_SIZE) +#endif + +#ifndef USE_RISC_NOOP_AUDIO +#define AUDIO_RISC_DMA_BUF_SIZE ( LINES_PER_AUDIO_BUFFER*RISC_READ_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + RISC_JUMP_INSTRUCTION_SIZE) +#endif + +static int _line_size; +char *_defaultAudioName = "/root/audioGOOD.wav"; diff --git a/linux/drivers/staging/cx25821/cx25821-audio.h b/linux/drivers/staging/cx25821/cx25821-audio.h new file mode 100644 index 000000000..503f42f03 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-audio.h @@ -0,0 +1,57 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CX25821_AUDIO_H__ +#define __CX25821_AUDIO_H__ + +#define USE_RISC_NOOP 1 +#define LINES_PER_BUFFER 15 +#define AUDIO_LINE_SIZE 128 + +//Number of buffer programs to use at once. +#define NUMBER_OF_PROGRAMS 8 + +//Max size of the RISC program for a buffer. - worst case is 2 writes per line +// Space is also added for the 4 no-op instructions added on the end. + +#ifndef USE_RISC_NOOP +#define MAX_BUFFER_PROGRAM_SIZE \ + (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE*4) +#endif + +// MAE 12 July 2005 Try to use NOOP RISC instruction instead +#ifdef USE_RISC_NOOP +#define MAX_BUFFER_PROGRAM_SIZE \ + (2*LINES_PER_BUFFER*RISC_WRITE_INSTRUCTION_SIZE + RISC_NOOP_INSTRUCTION_SIZE*4) +#endif + +//Sizes of various instructions in bytes. Used when adding instructions. +#define RISC_WRITE_INSTRUCTION_SIZE 12 +#define RISC_JUMP_INSTRUCTION_SIZE 12 +#define RISC_SKIP_INSTRUCTION_SIZE 4 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_NOOP_INSTRUCTION_SIZE 4 + +#define MAX_AUDIO_DMA_BUFFER_SIZE (MAX_BUFFER_PROGRAM_SIZE * NUMBER_OF_PROGRAMS + RISC_SYNC_INSTRUCTION_SIZE) + +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-audups11.c b/linux/drivers/staging/cx25821/cx25821-audups11.c new file mode 100644 index 000000000..f78b8912d --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-audups11.c @@ -0,0 +1,434 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH11]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH11]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH11] + && h->video_dev[SRAM_CH11]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = 10; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO11)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO11)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + //cx_write(channel11->dma_ctl, 0); + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO11)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO11); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO11)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO11); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vidq.field = f->fmt.pix.field; + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + return 0; +} + +static long video_ioctl_upstream11(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + printk + ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_AUDIO && command != UPSTREAM_STOP_AUDIO) { + return 0; + } + + dev->input_filename = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname = data_from_user->vid_stdname; + dev->pixel_format = data_from_user->pixel_format; + dev->channel_select = data_from_user->channel_select; + dev->command = data_from_user->command; + + switch (command) { + case UPSTREAM_START_AUDIO: + cx25821_start_upstream_audio(dev, data_from_user); + break; + + case UPSTREAM_STOP_AUDIO: + cx25821_stop_upstream_audio(dev); + break; + } + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + return 0; +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl_upstream11, +}; + +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, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template11 = { + .name = "cx25821-audioupstream", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-biffuncs.h b/linux/drivers/staging/cx25821/cx25821-biffuncs.h new file mode 100644 index 000000000..9326a7c72 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-biffuncs.h @@ -0,0 +1,45 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _BITFUNCS_H +#define _BITFUNCS_H + +#define SetBit(Bit) (1 << Bit) + +inline u8 getBit(u32 sample, u8 index) +{ + return (u8) ((sample >> index) & 1); +} + +inline u32 clearBitAtPos(u32 value, u8 bit) +{ + return value & ~(1 << bit); +} + +inline u32 setBitAtPos(u32 sample, u8 bit) +{ + sample |= (1 << bit); + return sample; + +} + +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-cards.c b/linux/drivers/staging/cx25821/cx25821-cards.c new file mode 100644 index 000000000..d4f663908 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-cards.c @@ -0,0 +1,71 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <media/cx25840.h> + +#include "compat.h" +#include "cx25821.h" +#include "tuner-xc2028.h" + +// board config info + +struct cx25821_board cx25821_boards[] = { + [UNKNOWN_BOARD] = { + .name = "UNKNOWN/GENERIC", + // Ensure safe default for unknown boards + .clk_freq = 0, + }, + + [CX25821_BOARD] = { + .name = "CX25821", + .portb = CX25821_RAW, + .portc = CX25821_264, + .input[0].type = CX25821_VMUX_COMPOSITE, + }, + +}; + +const unsigned int cx25821_bcount = ARRAY_SIZE(cx25821_boards); + +struct cx25821_subid cx25821_subids[] = { + { + .subvendor = 0x14f1, + .subdevice = 0x0920, + .card = CX25821_BOARD, + }, +}; + +void cx25821_card_setup(struct cx25821_dev *dev) +{ + static u8 eeprom[256]; + + if (dev->i2c_bus[0].i2c_rc == 0) { + dev->i2c_bus[0].i2c_client.addr = 0xa0 >> 1; + tveeprom_read(&dev->i2c_bus[0].i2c_client, eeprom, + sizeof(eeprom)); + } +} diff --git a/linux/drivers/staging/cx25821/cx25821-core.c b/linux/drivers/staging/cx25821/cx25821-core.c new file mode 100644 index 000000000..c820407a0 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-core.c @@ -0,0 +1,1572 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/i2c.h> +#include "cx25821.h" +#include "cx25821-sram.h" +#include "cx25821-video.h" + +MODULE_DESCRIPTION("Driver for Athena cards"); +MODULE_AUTHOR("Shu Lin - Hiep Huynh"); +MODULE_LICENSE("GPL"); + +struct list_head cx25821_devlist; + +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +static unsigned int card[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; +module_param_array(card, int, NULL, 0444); +MODULE_PARM_DESC(card, "card type"); + +static unsigned int cx25821_devcount = 0; + +static DEFINE_MUTEX(devlist); +LIST_HEAD(cx25821_devlist); + +struct sram_channel cx25821_sram_channels[] = { + [SRAM_CH00] = { + .i = SRAM_CH00, + .name = "VID A", + .cmds_start = VID_A_DOWN_CMDS, + .ctrl_start = VID_A_IQ, + .cdt = VID_A_CDT, + .fifo_start = VID_A_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA1_PTR1, + .ptr2_reg = DMA1_PTR2, + .cnt1_reg = DMA1_CNT1, + .cnt2_reg = DMA1_CNT2, + .int_msk = VID_A_INT_MSK, + .int_stat = VID_A_INT_STAT, + .int_mstat = VID_A_INT_MSTAT, + .dma_ctl = VID_DST_A_DMA_CTL, + .gpcnt_ctl = VID_DST_A_GPCNT_CTL, + .gpcnt = VID_DST_A_GPCNT, + .vip_ctl = VID_DST_A_VIP_CTL, + .pix_frmt = VID_DST_A_PIX_FRMT, + }, + + [SRAM_CH01] = { + .i = SRAM_CH01, + .name = "VID B", + .cmds_start = VID_B_DOWN_CMDS, + .ctrl_start = VID_B_IQ, + .cdt = VID_B_CDT, + .fifo_start = VID_B_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA2_PTR1, + .ptr2_reg = DMA2_PTR2, + .cnt1_reg = DMA2_CNT1, + .cnt2_reg = DMA2_CNT2, + .int_msk = VID_B_INT_MSK, + .int_stat = VID_B_INT_STAT, + .int_mstat = VID_B_INT_MSTAT, + .dma_ctl = VID_DST_B_DMA_CTL, + .gpcnt_ctl = VID_DST_B_GPCNT_CTL, + .gpcnt = VID_DST_B_GPCNT, + .vip_ctl = VID_DST_B_VIP_CTL, + .pix_frmt = VID_DST_B_PIX_FRMT, + }, + + [SRAM_CH02] = { + .i = SRAM_CH02, + .name = "VID C", + .cmds_start = VID_C_DOWN_CMDS, + .ctrl_start = VID_C_IQ, + .cdt = VID_C_CDT, + .fifo_start = VID_C_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA3_PTR1, + .ptr2_reg = DMA3_PTR2, + .cnt1_reg = DMA3_CNT1, + .cnt2_reg = DMA3_CNT2, + .int_msk = VID_C_INT_MSK, + .int_stat = VID_C_INT_STAT, + .int_mstat = VID_C_INT_MSTAT, + .dma_ctl = VID_DST_C_DMA_CTL, + .gpcnt_ctl = VID_DST_C_GPCNT_CTL, + .gpcnt = VID_DST_C_GPCNT, + .vip_ctl = VID_DST_C_VIP_CTL, + .pix_frmt = VID_DST_C_PIX_FRMT, + }, + + [SRAM_CH03] = { + .i = SRAM_CH03, + .name = "VID D", + .cmds_start = VID_D_DOWN_CMDS, + .ctrl_start = VID_D_IQ, + .cdt = VID_D_CDT, + .fifo_start = VID_D_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA4_PTR1, + .ptr2_reg = DMA4_PTR2, + .cnt1_reg = DMA4_CNT1, + .cnt2_reg = DMA4_CNT2, + .int_msk = VID_D_INT_MSK, + .int_stat = VID_D_INT_STAT, + .int_mstat = VID_D_INT_MSTAT, + .dma_ctl = VID_DST_D_DMA_CTL, + .gpcnt_ctl = VID_DST_D_GPCNT_CTL, + .gpcnt = VID_DST_D_GPCNT, + .vip_ctl = VID_DST_D_VIP_CTL, + .pix_frmt = VID_DST_D_PIX_FRMT, + }, + + [SRAM_CH04] = { + .i = SRAM_CH04, + .name = "VID E", + .cmds_start = VID_E_DOWN_CMDS, + .ctrl_start = VID_E_IQ, + .cdt = VID_E_CDT, + .fifo_start = VID_E_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA5_PTR1, + .ptr2_reg = DMA5_PTR2, + .cnt1_reg = DMA5_CNT1, + .cnt2_reg = DMA5_CNT2, + .int_msk = VID_E_INT_MSK, + .int_stat = VID_E_INT_STAT, + .int_mstat = VID_E_INT_MSTAT, + .dma_ctl = VID_DST_E_DMA_CTL, + .gpcnt_ctl = VID_DST_E_GPCNT_CTL, + .gpcnt = VID_DST_E_GPCNT, + .vip_ctl = VID_DST_E_VIP_CTL, + .pix_frmt = VID_DST_E_PIX_FRMT, + }, + + [SRAM_CH05] = { + .i = SRAM_CH05, + .name = "VID F", + .cmds_start = VID_F_DOWN_CMDS, + .ctrl_start = VID_F_IQ, + .cdt = VID_F_CDT, + .fifo_start = VID_F_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA6_PTR1, + .ptr2_reg = DMA6_PTR2, + .cnt1_reg = DMA6_CNT1, + .cnt2_reg = DMA6_CNT2, + .int_msk = VID_F_INT_MSK, + .int_stat = VID_F_INT_STAT, + .int_mstat = VID_F_INT_MSTAT, + .dma_ctl = VID_DST_F_DMA_CTL, + .gpcnt_ctl = VID_DST_F_GPCNT_CTL, + .gpcnt = VID_DST_F_GPCNT, + .vip_ctl = VID_DST_F_VIP_CTL, + .pix_frmt = VID_DST_F_PIX_FRMT, + }, + + [SRAM_CH06] = { + .i = SRAM_CH06, + .name = "VID G", + .cmds_start = VID_G_DOWN_CMDS, + .ctrl_start = VID_G_IQ, + .cdt = VID_G_CDT, + .fifo_start = VID_G_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA7_PTR1, + .ptr2_reg = DMA7_PTR2, + .cnt1_reg = DMA7_CNT1, + .cnt2_reg = DMA7_CNT2, + .int_msk = VID_G_INT_MSK, + .int_stat = VID_G_INT_STAT, + .int_mstat = VID_G_INT_MSTAT, + .dma_ctl = VID_DST_G_DMA_CTL, + .gpcnt_ctl = VID_DST_G_GPCNT_CTL, + .gpcnt = VID_DST_G_GPCNT, + .vip_ctl = VID_DST_G_VIP_CTL, + .pix_frmt = VID_DST_G_PIX_FRMT, + }, + + [SRAM_CH07] = { + .i = SRAM_CH07, + .name = "VID H", + .cmds_start = VID_H_DOWN_CMDS, + .ctrl_start = VID_H_IQ, + .cdt = VID_H_CDT, + .fifo_start = VID_H_DOWN_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA8_PTR1, + .ptr2_reg = DMA8_PTR2, + .cnt1_reg = DMA8_CNT1, + .cnt2_reg = DMA8_CNT2, + .int_msk = VID_H_INT_MSK, + .int_stat = VID_H_INT_STAT, + .int_mstat = VID_H_INT_MSTAT, + .dma_ctl = VID_DST_H_DMA_CTL, + .gpcnt_ctl = VID_DST_H_GPCNT_CTL, + .gpcnt = VID_DST_H_GPCNT, + .vip_ctl = VID_DST_H_VIP_CTL, + .pix_frmt = VID_DST_H_PIX_FRMT, + }, + + [SRAM_CH08] = { + .name = "audio from", + .cmds_start = AUD_A_DOWN_CMDS, + .ctrl_start = AUD_A_IQ, + .cdt = AUD_A_CDT, + .fifo_start = AUD_A_DOWN_CLUSTER_1, + .fifo_size = AUDIO_CLUSTER_SIZE * 3, + .ptr1_reg = DMA17_PTR1, + .ptr2_reg = DMA17_PTR2, + .cnt1_reg = DMA17_CNT1, + .cnt2_reg = DMA17_CNT2, + }, + + [SRAM_CH09] = { + .i = SRAM_CH09, + .name = "VID Upstream I", + .cmds_start = VID_I_UP_CMDS, + .ctrl_start = VID_I_IQ, + .cdt = VID_I_CDT, + .fifo_start = VID_I_UP_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA15_PTR1, + .ptr2_reg = DMA15_PTR2, + .cnt1_reg = DMA15_CNT1, + .cnt2_reg = DMA15_CNT2, + .int_msk = VID_I_INT_MSK, + .int_stat = VID_I_INT_STAT, + .int_mstat = VID_I_INT_MSTAT, + .dma_ctl = VID_SRC_I_DMA_CTL, + .gpcnt_ctl = VID_SRC_I_GPCNT_CTL, + .gpcnt = VID_SRC_I_GPCNT, + + .vid_fmt_ctl = VID_SRC_I_FMT_CTL, + .vid_active_ctl1 = VID_SRC_I_ACTIVE_CTL1, + .vid_active_ctl2 = VID_SRC_I_ACTIVE_CTL2, + .vid_cdt_size = VID_SRC_I_CDT_SZ, + .irq_bit = 8, + }, + + [SRAM_CH10] = { + .i = SRAM_CH10, + .name = "VID Upstream J", + .cmds_start = VID_J_UP_CMDS, + .ctrl_start = VID_J_IQ, + .cdt = VID_J_CDT, + .fifo_start = VID_J_UP_CLUSTER_1, + .fifo_size = (VID_CLUSTER_SIZE << 2), + .ptr1_reg = DMA16_PTR1, + .ptr2_reg = DMA16_PTR2, + .cnt1_reg = DMA16_CNT1, + .cnt2_reg = DMA16_CNT2, + .int_msk = VID_J_INT_MSK, + .int_stat = VID_J_INT_STAT, + .int_mstat = VID_J_INT_MSTAT, + .dma_ctl = VID_SRC_J_DMA_CTL, + .gpcnt_ctl = VID_SRC_J_GPCNT_CTL, + .gpcnt = VID_SRC_J_GPCNT, + + .vid_fmt_ctl = VID_SRC_J_FMT_CTL, + .vid_active_ctl1 = VID_SRC_J_ACTIVE_CTL1, + .vid_active_ctl2 = VID_SRC_J_ACTIVE_CTL2, + .vid_cdt_size = VID_SRC_J_CDT_SZ, + .irq_bit = 9, + }, + + [SRAM_CH11] = { + .i = SRAM_CH11, + .name = "Audio Upstream Channel B", + .cmds_start = AUD_B_UP_CMDS, + .ctrl_start = AUD_B_IQ, + .cdt = AUD_B_CDT, + .fifo_start = AUD_B_UP_CLUSTER_1, + .fifo_size = (AUDIO_CLUSTER_SIZE * 3), + .ptr1_reg = DMA22_PTR1, + .ptr2_reg = DMA22_PTR2, + .cnt1_reg = DMA22_CNT1, + .cnt2_reg = DMA22_CNT2, + .int_msk = AUD_B_INT_MSK, + .int_stat = AUD_B_INT_STAT, + .int_mstat = AUD_B_INT_MSTAT, + .dma_ctl = AUD_INT_DMA_CTL, + .gpcnt_ctl = AUD_B_GPCNT_CTL, + .gpcnt = AUD_B_GPCNT, + .aud_length = AUD_B_LNGTH, + .aud_cfg = AUD_B_CFG, + .fld_aud_fifo_en = FLD_AUD_SRC_B_FIFO_EN, + .fld_aud_risc_en = FLD_AUD_SRC_B_RISC_EN, + .irq_bit = 11, + }, +}; + +struct sram_channel *channel0 = &cx25821_sram_channels[SRAM_CH00]; +struct sram_channel *channel1 = &cx25821_sram_channels[SRAM_CH01]; +struct sram_channel *channel2 = &cx25821_sram_channels[SRAM_CH02]; +struct sram_channel *channel3 = &cx25821_sram_channels[SRAM_CH03]; +struct sram_channel *channel4 = &cx25821_sram_channels[SRAM_CH04]; +struct sram_channel *channel5 = &cx25821_sram_channels[SRAM_CH05]; +struct sram_channel *channel6 = &cx25821_sram_channels[SRAM_CH06]; +struct sram_channel *channel7 = &cx25821_sram_channels[SRAM_CH07]; +struct sram_channel *channel9 = &cx25821_sram_channels[SRAM_CH09]; +struct sram_channel *channel10 = &cx25821_sram_channels[SRAM_CH10]; +struct sram_channel *channel11 = &cx25821_sram_channels[SRAM_CH11]; + +struct cx25821_dmaqueue mpegq; + +static int cx25821_risc_decode(u32 risc) +{ + static char *instr[16] = { + [RISC_SYNC >> 28] = "sync", + [RISC_WRITE >> 28] = "write", + [RISC_WRITEC >> 28] = "writec", + [RISC_READ >> 28] = "read", + [RISC_READC >> 28] = "readc", + [RISC_JUMP >> 28] = "jump", + [RISC_SKIP >> 28] = "skip", + [RISC_WRITERM >> 28] = "writerm", + [RISC_WRITECM >> 28] = "writecm", + [RISC_WRITECR >> 28] = "writecr", + }; + static int incr[16] = { + [RISC_WRITE >> 28] = 3, + [RISC_JUMP >> 28] = 3, + [RISC_SKIP >> 28] = 1, + [RISC_SYNC >> 28] = 1, + [RISC_WRITERM >> 28] = 3, + [RISC_WRITECM >> 28] = 3, + [RISC_WRITECR >> 28] = 4, + }; + static char *bits[] = { + "12", "13", "14", "resync", + "cnt0", "cnt1", "18", "19", + "20", "21", "22", "23", + "irq1", "irq2", "eol", "sol", + }; + int i; + + printk("0x%08x [ %s", risc, + instr[risc >> 28] ? instr[risc >> 28] : "INVALID"); + for (i = ARRAY_SIZE(bits) - 1; i >= 0; i--) { + if (risc & (1 << (i + 12))) + printk(" %s", bits[i]); + } + printk(" count=%d ]\n", risc & 0xfff); + return incr[risc >> 28] ? incr[risc >> 28] : 1; +} + +static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + return cx_read(bus->reg_stat) & 0x01; +} + +void cx_i2c_read_print(struct cx25821_dev *dev, u32 reg, const char *reg_string) +{ + int tmp = 0; + u32 value = 0; + + value = cx25821_i2c_read(&dev->i2c_bus[0], reg, &tmp); +} + +static void cx25821_registers_init(struct cx25821_dev *dev) +{ + u32 tmp; + + // enable RUN_RISC in Pecos + cx_write(DEV_CNTRL2, 0x20); + + // Set the master PCI interrupt masks to enable video, audio, MBIF, and GPIO interrupts + // I2C interrupt masking is handled by the I2C objects themselves. + cx_write(PCI_INT_MSK, 0x2001FFFF); + + tmp = cx_read(RDR_TLCTL0); + tmp &= ~FLD_CFG_RCB_CK_EN; // Clear the RCB_CK_EN bit + cx_write(RDR_TLCTL0, tmp); + + // PLL-A setting for the Audio Master Clock + cx_write(PLL_A_INT_FRAC, 0x9807A58B); + + // PLL_A_POST = 0x1C, PLL_A_OUT_TO_PIN = 0x1 + cx_write(PLL_A_POST_STAT_BIST, 0x8000019C); + + // clear reset bit [31] + tmp = cx_read(PLL_A_INT_FRAC); + cx_write(PLL_A_INT_FRAC, tmp & 0x7FFFFFFF); + + // PLL-B setting for Mobilygen Host Bus Interface + cx_write(PLL_B_INT_FRAC, 0x9883A86F); + + // PLL_B_POST = 0xD, PLL_B_OUT_TO_PIN = 0x0 + cx_write(PLL_B_POST_STAT_BIST, 0x8000018D); + + // clear reset bit [31] + tmp = cx_read(PLL_B_INT_FRAC); + cx_write(PLL_B_INT_FRAC, tmp & 0x7FFFFFFF); + + // PLL-C setting for video upstream channel + cx_write(PLL_C_INT_FRAC, 0x96A0EA3F); + + // PLL_C_POST = 0x3, PLL_C_OUT_TO_PIN = 0x0 + cx_write(PLL_C_POST_STAT_BIST, 0x80000103); + + // clear reset bit [31] + tmp = cx_read(PLL_C_INT_FRAC); + cx_write(PLL_C_INT_FRAC, tmp & 0x7FFFFFFF); + + // PLL-D setting for audio upstream channel + cx_write(PLL_D_INT_FRAC, 0x98757F5B); + + // PLL_D_POST = 0x13, PLL_D_OUT_TO_PIN = 0x0 + cx_write(PLL_D_POST_STAT_BIST, 0x80000113); + + // clear reset bit [31] + tmp = cx_read(PLL_D_INT_FRAC); + cx_write(PLL_D_INT_FRAC, tmp & 0x7FFFFFFF); + + // This selects the PLL C clock source for the video upstream channel I and J + tmp = cx_read(VID_CH_CLK_SEL); + cx_write(VID_CH_CLK_SEL, (tmp & 0x00FFFFFF) | 0x24000000); + + // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + //select 656/VIP DST for downstream Channel A - C + tmp = cx_read(VID_CH_MODE_SEL); + //cx_write( VID_CH_MODE_SEL, tmp | 0x1B0001FF); + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); + + // enables 656 port I and J as output + tmp = cx_read(CLK_RST); + tmp |= FLD_USE_ALT_PLL_REF; // use external ALT_PLL_REF pin as its reference clock instead + cx_write(CLK_RST, tmp & ~(FLD_VID_I_CLK_NOE | FLD_VID_J_CLK_NOE)); + + mdelay(100); +} + +int cx25821_sram_channel_setup(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 4) { + lines = 4; + } + + BUG_ON(lines < 2); + + cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + cx_write(8 + 4, 8); + cx_write(8 + 8, 0); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + //init the first cdt buffer + for (i = 0; i < 128; i++) + cx_write(ch->fifo_start + 4 * i, i); + + /* write CMDS */ + if (ch->jumponly) { + cx_write(ch->cmds_start + 0, 8); + } else { + cx_write(ch->cmds_start + 0, risc); + } + + cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, (lines * 16) >> 3); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + if (ch->jumponly) + cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); + else + cx_write(ch->cmds_start + 20, 64 >> 2); + + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, (lines * 16) >> 3); + cx_write(ch->cnt1_reg, (bpl >> 3) - 1); + + return 0; +} + +int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 3) { + lines = 3; //for AUDIO + } + + BUG_ON(lines < 2); + + cx_write(8 + 0, RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + cx_write(8 + 4, 8); + cx_write(8 + 8, 0); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + /* write CMDS */ + if (ch->jumponly) { + cx_write(ch->cmds_start + 0, 8); + } else { + cx_write(ch->cmds_start + 0, risc); + } + + cx_write(ch->cmds_start + 4, 0); /* 64 bits 63-32 */ + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, (lines * 16) >> 3); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + //IQ size + if (ch->jumponly) { + cx_write(ch->cmds_start + 20, 0x80000000 | (64 >> 2)); + } else { + cx_write(ch->cmds_start + 20, 64 >> 2); + } + + //zero out + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, (lines * 16) >> 3); + cx_write(ch->cnt1_reg, (bpl >> 3) - 1); + + return 0; +} + +void cx25821_sram_channel_dump(struct cx25821_dev *dev, struct sram_channel *ch) +{ + static char *name[] = { + "init risc lo", + "init risc hi", + "cdt base", + "cdt size", + "iq base", + "iq size", + "risc pc lo", + "risc pc hi", + "iq wr ptr", + "iq rd ptr", + "cdt current", + "pci target lo", + "pci target hi", + "line / byte", + }; + u32 risc; + unsigned int i, j, n; + + printk(KERN_WARNING "%s: %s - dma channel status dump\n", dev->name, + ch->name); + for (i = 0; i < ARRAY_SIZE(name); i++) + printk(KERN_WARNING "cmds + 0x%2x: %-15s: 0x%08x\n", i * 4, + name[i], cx_read(ch->cmds_start + 4 * i)); + + j = i * 4; + for (i = 0; i < 4;) { + risc = cx_read(ch->cmds_start + 4 * (i + 14)); + printk(KERN_WARNING "cmds + 0x%2x: risc%d: ", j + i * 4, i); + i += cx25821_risc_decode(risc); + } + + for (i = 0; i < (64 >> 2); i += n) { + risc = cx_read(ch->ctrl_start + 4 * i); + /* No consideration for bits 63-32 */ + + printk(KERN_WARNING "ctrl + 0x%2x (0x%08x): iq %x: ", i * 4, + ch->ctrl_start + 4 * i, i); + n = cx25821_risc_decode(risc); + for (j = 1; j < n; j++) { + risc = cx_read(ch->ctrl_start + 4 * (i + j)); + printk(KERN_WARNING + "ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n", + 4 * (i + j), i + j, risc, j); + } + } + + printk(KERN_WARNING " : fifo: 0x%08x -> 0x%x\n", + ch->fifo_start, ch->fifo_start + ch->fifo_size); + printk(KERN_WARNING " : ctrl: 0x%08x -> 0x%x\n", + ch->ctrl_start, ch->ctrl_start + 6 * 16); + printk(KERN_WARNING " : ptr1_reg: 0x%08x\n", + cx_read(ch->ptr1_reg)); + printk(KERN_WARNING " : ptr2_reg: 0x%08x\n", + cx_read(ch->ptr2_reg)); + printk(KERN_WARNING " : cnt1_reg: 0x%08x\n", + cx_read(ch->cnt1_reg)); + printk(KERN_WARNING " : cnt2_reg: 0x%08x\n", + cx_read(ch->cnt2_reg)); +} + +void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, + struct sram_channel *ch) +{ + static char *name[] = { + "init risc lo", + "init risc hi", + "cdt base", + "cdt size", + "iq base", + "iq size", + "risc pc lo", + "risc pc hi", + "iq wr ptr", + "iq rd ptr", + "cdt current", + "pci target lo", + "pci target hi", + "line / byte", + }; + + u32 risc, value, tmp; + unsigned int i, j, n; + + printk(KERN_INFO "\n%s: %s - dma Audio channel status dump\n", + dev->name, ch->name); + + for (i = 0; i < ARRAY_SIZE(name); i++) + printk(KERN_INFO "%s: cmds + 0x%2x: %-15s: 0x%08x\n", + dev->name, i * 4, name[i], + cx_read(ch->cmds_start + 4 * i)); + + j = i * 4; + for (i = 0; i < 4;) { + risc = cx_read(ch->cmds_start + 4 * (i + 14)); + printk(KERN_WARNING "cmds + 0x%2x: risc%d: ", j + i * 4, i); + i += cx25821_risc_decode(risc); + } + + for (i = 0; i < (64 >> 2); i += n) { + risc = cx_read(ch->ctrl_start + 4 * i); + /* No consideration for bits 63-32 */ + + printk(KERN_WARNING "ctrl + 0x%2x (0x%08x): iq %x: ", i * 4, + ch->ctrl_start + 4 * i, i); + n = cx25821_risc_decode(risc); + + for (j = 1; j < n; j++) { + risc = cx_read(ch->ctrl_start + 4 * (i + j)); + printk(KERN_WARNING + "ctrl + 0x%2x : iq %x: 0x%08x [ arg #%d ]\n", + 4 * (i + j), i + j, risc, j); + } + } + + printk(KERN_WARNING " : fifo: 0x%08x -> 0x%x\n", + ch->fifo_start, ch->fifo_start + ch->fifo_size); + printk(KERN_WARNING " : ctrl: 0x%08x -> 0x%x\n", + ch->ctrl_start, ch->ctrl_start + 6 * 16); + printk(KERN_WARNING " : ptr1_reg: 0x%08x\n", + cx_read(ch->ptr1_reg)); + printk(KERN_WARNING " : ptr2_reg: 0x%08x\n", + cx_read(ch->ptr2_reg)); + printk(KERN_WARNING " : cnt1_reg: 0x%08x\n", + cx_read(ch->cnt1_reg)); + printk(KERN_WARNING " : cnt2_reg: 0x%08x\n", + cx_read(ch->cnt2_reg)); + + for (i = 0; i < 4; i++) { + risc = cx_read(ch->cmds_start + 56 + (i * 4)); + printk(KERN_WARNING "instruction %d = 0x%x\n", i, risc); + } + + //read data from the first cdt buffer + risc = cx_read(AUD_A_CDT); + printk(KERN_WARNING "\nread cdt loc=0x%x\n", risc); + for (i = 0; i < 8; i++) { + n = cx_read(risc + i * 4); + printk(KERN_WARNING "0x%x ", n); + } + printk(KERN_WARNING "\n\n"); + + value = cx_read(CLK_RST); + CX25821_INFO(" CLK_RST = 0x%x \n\n", value); + + value = cx_read(PLL_A_POST_STAT_BIST); + CX25821_INFO(" PLL_A_POST_STAT_BIST = 0x%x \n\n", value); + value = cx_read(PLL_A_INT_FRAC); + CX25821_INFO(" PLL_A_INT_FRAC = 0x%x \n\n", value); + + value = cx_read(PLL_B_POST_STAT_BIST); + CX25821_INFO(" PLL_B_POST_STAT_BIST = 0x%x \n\n", value); + value = cx_read(PLL_B_INT_FRAC); + CX25821_INFO(" PLL_B_INT_FRAC = 0x%x \n\n", value); + + value = cx_read(PLL_C_POST_STAT_BIST); + CX25821_INFO(" PLL_C_POST_STAT_BIST = 0x%x \n\n", value); + value = cx_read(PLL_C_INT_FRAC); + CX25821_INFO(" PLL_C_INT_FRAC = 0x%x \n\n", value); + + value = cx_read(PLL_D_POST_STAT_BIST); + CX25821_INFO(" PLL_D_POST_STAT_BIST = 0x%x \n\n", value); + value = cx_read(PLL_D_INT_FRAC); + CX25821_INFO(" PLL_D_INT_FRAC = 0x%x \n\n", value); + + value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); + CX25821_INFO(" AFE_AB_DIAG_CTRL (0x10900090) = 0x%x \n\n", value); +} + +static void cx25821_shutdown(struct cx25821_dev *dev) +{ + int i; + + /* disable RISC controller */ + cx_write(DEV_CNTRL2, 0); + + /* Disable Video A/B activity */ + for (i = 0; i < VID_CHANNEL_NUM; i++) { + cx_write(dev->sram_channels[i].dma_ctl, 0); + cx_write(dev->sram_channels[i].int_msk, 0); + } + + for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J; + i++) { + cx_write(dev->sram_channels[i].dma_ctl, 0); + cx_write(dev->sram_channels[i].int_msk, 0); + } + + /* Disable Audio activity */ + cx_write(AUD_INT_DMA_CTL, 0); + + /* Disable Serial port */ + cx_write(UART_CTL, 0); + + /* Disable Interrupts */ + cx_write(PCI_INT_MSK, 0); + cx_write(AUD_A_INT_MSK, 0); +} + +void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel_select, + u32 format) +{ + struct sram_channel *ch; + + if (channel_select <= 7 && channel_select >= 0) { + ch = &cx25821_sram_channels[channel_select]; + cx_write(ch->pix_frmt, format); + dev->pixel_formats[channel_select] = format; + } +} + +static void cx25821_set_vip_mode(struct cx25821_dev *dev, + struct sram_channel *ch) +{ + cx_write(ch->pix_frmt, PIXEL_FRMT_422); + cx_write(ch->vip_ctl, PIXEL_ENGINE_VIP1); +} + +static void cx25821_initialize(struct cx25821_dev *dev) +{ + int i; + + dprintk(1, "%s()\n", __func__); + + cx25821_shutdown(dev); + cx_write(PCI_INT_STAT, 0xffffffff); + + for (i = 0; i < VID_CHANNEL_NUM; i++) + cx_write(dev->sram_channels[i].int_stat, 0xffffffff); + + cx_write(AUD_A_INT_STAT, 0xffffffff); + cx_write(AUD_B_INT_STAT, 0xffffffff); + cx_write(AUD_C_INT_STAT, 0xffffffff); + cx_write(AUD_D_INT_STAT, 0xffffffff); + cx_write(AUD_E_INT_STAT, 0xffffffff); + + cx_write(CLK_DELAY, cx_read(CLK_DELAY) & 0x80000000); + cx_write(PAD_CTRL, 0x12); //for I2C + cx25821_registers_init(dev); //init Pecos registers + mdelay(100); + + for (i = 0; i < VID_CHANNEL_NUM; i++) { + cx25821_set_vip_mode(dev, &dev->sram_channels[i]); + cx25821_sram_channel_setup(dev, &dev->sram_channels[i], 1440, + 0); + dev->pixel_formats[i] = PIXEL_FRMT_422; + dev->use_cif_resolution[i] = FALSE; + } + + //Probably only affect Downstream + for (i = VID_UPSTREAM_SRAM_CHANNEL_I; i <= VID_UPSTREAM_SRAM_CHANNEL_J; + i++) { + cx25821_set_vip_mode(dev, &dev->sram_channels[i]); + } + + cx25821_sram_channel_setup_audio(dev, &dev->sram_channels[SRAM_CH08], + 128, 0); + + cx25821_gpio_init(dev); +} + +static int get_resources(struct cx25821_dev *dev) +{ + if (request_mem_region + (pci_resource_start(dev->pci, 0), pci_resource_len(dev->pci, 0), + dev->name)) + return 0; + + printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n", + dev->name, (unsigned long long)pci_resource_start(dev->pci, 0)); + + return -EBUSY; +} + +static void cx25821_dev_checkrevision(struct cx25821_dev *dev) +{ + dev->hwrevision = cx_read(RDR_CFG2) & 0xff; + + printk(KERN_INFO "%s() Hardware revision = 0x%02x\n", __func__, + dev->hwrevision); +} + +static void cx25821_iounmap(struct cx25821_dev *dev) +{ + if (dev == NULL) + return; + + /* Releasing IO memory */ + if (dev->lmmio != NULL) { + CX25821_INFO("Releasing lmmio.\n"); + iounmap(dev->lmmio); + dev->lmmio = NULL; + } +} + +static int cx25821_dev_setup(struct cx25821_dev *dev) +{ + int io_size = 0, i; + + struct video_device *video_template[] = { + &cx25821_video_template0, + &cx25821_video_template1, + &cx25821_video_template2, + &cx25821_video_template3, + &cx25821_video_template4, + &cx25821_video_template5, + &cx25821_video_template6, + &cx25821_video_template7, + &cx25821_video_template9, + &cx25821_video_template10, + &cx25821_video_template11, + &cx25821_videoioctl_template, + }; + + printk(KERN_INFO "\n***********************************\n"); + printk(KERN_INFO "cx25821 set up\n"); + printk(KERN_INFO "***********************************\n\n"); + + mutex_init(&dev->lock); + + atomic_inc(&dev->refcount); + + dev->nr = ++cx25821_devcount; + sprintf(dev->name, "cx25821[%d]", dev->nr); + + mutex_lock(&devlist); + list_add_tail(&dev->devlist, &cx25821_devlist); + mutex_unlock(&devlist); + + strcpy(cx25821_boards[UNKNOWN_BOARD].name, "unknown"); + strcpy(cx25821_boards[CX25821_BOARD].name, "cx25821"); + + if (dev->pci->device != 0x8210) { + printk(KERN_INFO + "%s() Exiting. Incorrect Hardware device = 0x%02x\n", + __func__, dev->pci->device); + return -1; + } else { + printk(KERN_INFO "Athena Hardware device = 0x%02x\n", + dev->pci->device); + } + + /* Apply a sensible clock frequency for the PCIe bridge */ + dev->clk_freq = 28000000; + dev->sram_channels = cx25821_sram_channels; + + if (dev->nr > 1) { + CX25821_INFO("dev->nr > 1!"); + } + + /* board config */ + dev->board = 1; //card[dev->nr]; + dev->_max_num_decoders = MAX_DECODERS; + + dev->pci_bus = dev->pci->bus->number; + dev->pci_slot = PCI_SLOT(dev->pci->devfn); + dev->pci_irqmask = 0x001f00; + + /* External Master 1 Bus */ + dev->i2c_bus[0].nr = 0; + dev->i2c_bus[0].dev = dev; + dev->i2c_bus[0].reg_stat = I2C1_STAT; + dev->i2c_bus[0].reg_ctrl = I2C1_CTRL; + dev->i2c_bus[0].reg_addr = I2C1_ADDR; + dev->i2c_bus[0].reg_rdata = I2C1_RDATA; + dev->i2c_bus[0].reg_wdata = I2C1_WDATA; + dev->i2c_bus[0].i2c_period = (0x07 << 24); /* 1.95MHz */ + +#if 0 + /* External Master 2 Bus */ + dev->i2c_bus[1].nr = 1; + dev->i2c_bus[1].dev = dev; + dev->i2c_bus[1].reg_stat = I2C2_STAT; + dev->i2c_bus[1].reg_ctrl = I2C2_CTRL; + dev->i2c_bus[1].reg_addr = I2C2_ADDR; + dev->i2c_bus[1].reg_rdata = I2C2_RDATA; + dev->i2c_bus[1].reg_wdata = I2C2_WDATA; + dev->i2c_bus[1].i2c_period = (0x27 << 24); /* 100kHz */ + + /* Internal Master 3 Bus */ + dev->i2c_bus[2].nr = 2; + dev->i2c_bus[2].dev = dev; + dev->i2c_bus[2].reg_stat = I2C3_STAT; + dev->i2c_bus[2].reg_ctrl = I2C3_CTRL; + dev->i2c_bus[2].reg_addr = I2C3_ADDR; + dev->i2c_bus[2].reg_rdata = I2C3_RDATA; + dev->i2c_bus[2].reg_wdata = I2C3_WDATA; + dev->i2c_bus[2].i2c_period = (0x07 << 24); /* 1.95MHz */ +#endif + + if (get_resources(dev) < 0) { + printk(KERN_ERR "%s No more PCIe resources for " + "subsystem: %04x:%04x\n", + dev->name, dev->pci->subsystem_vendor, + dev->pci->subsystem_device); + + cx25821_devcount--; + return -ENODEV; + } + + /* PCIe stuff */ + dev->base_io_addr = pci_resource_start(dev->pci, 0); + io_size = pci_resource_len(dev->pci, 0); + + if (!dev->base_io_addr) { + CX25821_ERR("No PCI Memory resources, exiting!\n"); + return -ENODEV; + } + + dev->lmmio = ioremap(dev->base_io_addr, pci_resource_len(dev->pci, 0)); + + if (!dev->lmmio) { + CX25821_ERR + ("ioremap failed, maybe increasing __VMALLOC_RESERVE in page.h\n"); + cx25821_iounmap(dev); + return -ENOMEM; + } + + dev->bmmio = (u8 __iomem *) dev->lmmio; + + printk(KERN_INFO "%s: subsystem: %04x:%04x, board: %s [card=%d,%s]\n", + dev->name, dev->pci->subsystem_vendor, + dev->pci->subsystem_device, cx25821_boards[dev->board].name, + dev->board, card[dev->nr] == dev->board ? + "insmod option" : "autodetected"); + + /* init hardware */ + cx25821_initialize(dev); + + cx25821_i2c_register(&dev->i2c_bus[0]); +// cx25821_i2c_register(&dev->i2c_bus[1]); +// cx25821_i2c_register(&dev->i2c_bus[2]); + + CX25821_INFO("i2c register! bus->i2c_rc = %d\n", + dev->i2c_bus[0].i2c_rc); + + cx25821_card_setup(dev); + medusa_video_init(dev); + + for (i = 0; i < VID_CHANNEL_NUM; i++) { + if (cx25821_video_register(dev, i, video_template[i]) < 0) { + printk(KERN_ERR + "%s() Failed to register analog video adapters on VID channel %d\n", + __func__, i); + } + } + + for (i = VID_UPSTREAM_SRAM_CHANNEL_I; + i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) { + //Since we don't have template8 for Audio Downstream + if (cx25821_video_register(dev, i, video_template[i - 1]) < 0) { + printk(KERN_ERR + "%s() Failed to register analog video adapters for Upstream channel %d.\n", + __func__, i); + } + } + + // register IOCTL device + dev->ioctl_dev = + cx25821_vdev_init(dev, dev->pci, video_template[VIDEO_IOCTL_CH], + "video"); + + if (video_register_device + (dev->ioctl_dev, VFL_TYPE_GRABBER, VIDEO_IOCTL_CH) < 0) { + cx25821_videoioctl_unregister(dev); + printk(KERN_ERR + "%s() Failed to register video adapter for IOCTL so releasing.\n", + __func__); + } + + cx25821_dev_checkrevision(dev); + CX25821_INFO("cx25821 setup done!\n"); + + return 0; +} + +void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, + struct upstream_user_struct *up_data) +{ + dev->_isNTSC = !strcmp(dev->vid_stdname, "NTSC") ? 1 : 0; + + dev->tvnorm = !dev->_isNTSC ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; + medusa_set_videostandard(dev); + + cx25821_vidupstream_init_ch1(dev, dev->channel_select, + dev->pixel_format); +} + +void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, + struct upstream_user_struct *up_data) +{ + dev->_isNTSC_ch2 = !strcmp(dev->vid_stdname_ch2, "NTSC") ? 1 : 0; + + dev->tvnorm = !dev->_isNTSC_ch2 ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; + medusa_set_videostandard(dev); + + cx25821_vidupstream_init_ch2(dev, dev->channel_select_ch2, + dev->pixel_format_ch2); +} + +void cx25821_start_upstream_audio(struct cx25821_dev *dev, + struct upstream_user_struct *up_data) +{ + cx25821_audio_upstream_init(dev, AUDIO_UPSTREAM_SRAM_CHANNEL_B); +} + +void cx25821_dev_unregister(struct cx25821_dev *dev) +{ + int i; + + if (!dev->base_io_addr) + return; + + cx25821_free_mem_upstream_ch1(dev); + cx25821_free_mem_upstream_ch2(dev); + cx25821_free_mem_upstream_audio(dev); + + release_mem_region(dev->base_io_addr, pci_resource_len(dev->pci, 0)); + + if (!atomic_dec_and_test(&dev->refcount)) + return; + + for (i = 0; i < VID_CHANNEL_NUM; i++) + cx25821_video_unregister(dev, i); + + for (i = VID_UPSTREAM_SRAM_CHANNEL_I; + i <= AUDIO_UPSTREAM_SRAM_CHANNEL_B; i++) { + cx25821_video_unregister(dev, i); + } + + cx25821_videoioctl_unregister(dev); + + cx25821_i2c_unregister(&dev->i2c_bus[0]); + cx25821_iounmap(dev); +} + +static __le32 *cx25821_risc_field(__le32 * rp, struct scatterlist *sglist, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int padding, + unsigned int lines) +{ + struct scatterlist *sg; + unsigned int line, todo; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) { + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + } + + /* scan lines */ + sg = sglist; + for (line = 0; line < lines; line++) { + while (offset && offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + sg++; + } + if (bpl <= sg_dma_len(sg) - offset) { + /* fits into current chunk */ + *(rp++) = + cpu_to_le32(RISC_WRITE | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += bpl; + } else { + /* scanline needs to be split */ + todo = bpl; + *(rp++) = + cpu_to_le32(RISC_WRITE | RISC_SOL | + (sg_dma_len(sg) - offset)); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= (sg_dma_len(sg) - offset); + offset = 0; + sg++; + while (todo > sg_dma_len(sg)) { + *(rp++) = + cpu_to_le32(RISC_WRITE | sg_dma_len(sg)); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= sg_dma_len(sg); + sg++; + } + *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += todo; + } + + offset += padding; + } + + return rp; +} + +int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, + struct scatterlist *sglist, unsigned int top_offset, + unsigned int bottom_offset, unsigned int bpl, + unsigned int padding, unsigned int lines) +{ + u32 instructions; + u32 fields; + __le32 *rp; + int rc; + + fields = 0; + if (UNSET != top_offset) + fields++; + if (UNSET != bottom_offset) + fields++; + + /* estimate risc mem: worst case is one write per page border + + one write per scan line + syncs + jump (all 2 dwords). Padding + can cause next bpl to start close to a page border. First DMA + region may be smaller than PAGE_SIZE */ + /* write and jump need and extra dword */ + instructions = + fields * (1 + ((bpl + padding) * lines) / PAGE_SIZE + lines); + instructions += 2; + rc = btcx_riscmem_alloc(pci, risc, instructions * 12); + + if (rc < 0) + return rc; + + /* write risc instructions */ + rp = risc->cpu; + + if (UNSET != top_offset) { + rp = cx25821_risc_field(rp, sglist, top_offset, 0, bpl, padding, + lines); + } + + if (UNSET != bottom_offset) { + rp = cx25821_risc_field(rp, sglist, bottom_offset, 0x200, bpl, + padding, lines); + } + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + + return 0; +} + +static __le32 *cx25821_risc_field_audio(__le32 * rp, struct scatterlist *sglist, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int padding, + unsigned int lines, unsigned int lpi) +{ + struct scatterlist *sg; + unsigned int line, todo, sol; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + /* scan lines */ + sg = sglist; + for (line = 0; line < lines; line++) { + while (offset && offset >= sg_dma_len(sg)) { + offset -= sg_dma_len(sg); + sg++; + } + + if (lpi && line > 0 && !(line % lpi)) + sol = RISC_SOL | RISC_IRQ1 | RISC_CNT_INC; + else + sol = RISC_SOL; + + if (bpl <= sg_dma_len(sg) - offset) { + /* fits into current chunk */ + *(rp++) = + cpu_to_le32(RISC_WRITE | sol | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += bpl; + } else { + /* scanline needs to be split */ + todo = bpl; + *(rp++) = cpu_to_le32(RISC_WRITE | sol | + (sg_dma_len(sg) - offset)); + *(rp++) = cpu_to_le32(sg_dma_address(sg) + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= (sg_dma_len(sg) - offset); + offset = 0; + sg++; + while (todo > sg_dma_len(sg)) { + *(rp++) = cpu_to_le32(RISC_WRITE | + sg_dma_len(sg)); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + todo -= sg_dma_len(sg); + sg++; + } + *(rp++) = cpu_to_le32(RISC_WRITE | RISC_EOL | todo); + *(rp++) = cpu_to_le32(sg_dma_address(sg)); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + offset += todo; + } + offset += padding; + } + + return rp; +} + +int cx25821_risc_databuffer_audio(struct pci_dev *pci, + struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int bpl, + unsigned int lines, unsigned int lpi) +{ + u32 instructions; + __le32 *rp; + int rc; + + /* estimate risc mem: worst case is one write per page border + + one write per scan line + syncs + jump (all 2 dwords). Here + there is no padding and no sync. First DMA region may be smaller + than PAGE_SIZE */ + /* Jump and write need an extra dword */ + instructions = 1 + (bpl * lines) / PAGE_SIZE + lines; + instructions += 1; + + if ((rc = btcx_riscmem_alloc(pci, risc, instructions * 12)) < 0) + return rc; + + /* write risc instructions */ + rp = risc->cpu; + rp = cx25821_risc_field_audio(rp, sglist, 0, NO_SYNC_LINE, bpl, 0, + lines, lpi); + + /* save pointer to jmp instruction address */ + risc->jmp = rp; + BUG_ON((risc->jmp - risc->cpu + 2) * sizeof(*risc->cpu) > risc->size); + return 0; +} + +int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, + u32 reg, u32 mask, u32 value) +{ + __le32 *rp; + int rc; + + rc = btcx_riscmem_alloc(pci, risc, 4 * 16); + + if (rc < 0) + return rc; + + /* write risc instructions */ + rp = risc->cpu; + + *(rp++) = cpu_to_le32(RISC_WRITECR | RISC_IRQ1); + *(rp++) = cpu_to_le32(reg); + *(rp++) = cpu_to_le32(value); + *(rp++) = cpu_to_le32(mask); + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(risc->dma); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + return 0; +} + +void cx25821_free_buffer(struct videobuf_queue *q, struct cx25821_buffer *buf) +{ + struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + + BUG_ON(in_interrupt()); + videobuf_waiton(&buf->vb, 0, 0); + videobuf_dma_unmap(q, dma); + videobuf_dma_free(dma); + btcx_riscmem_free(to_pci_dev(q->dev), &buf->risc); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static irqreturn_t cx25821_irq(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 pci_status, pci_mask; + u32 vid_status; + int i, handled = 0; + u32 mask[8] = { 1, 2, 4, 8, 16, 32, 64, 128 }; + + pci_status = cx_read(PCI_INT_STAT); + pci_mask = cx_read(PCI_INT_MSK); + + if (pci_status == 0) + goto out; + + for (i = 0; i < VID_CHANNEL_NUM; i++) { + if (pci_status & mask[i]) { + vid_status = cx_read(dev->sram_channels[i].int_stat); + + if (vid_status) + handled += + cx25821_video_irq(dev, i, vid_status); + + cx_write(PCI_INT_STAT, mask[i]); + } + } + + out: + return IRQ_RETVAL(handled); +} + +void cx25821_print_irqbits(char *name, char *tag, char **strings, + int len, u32 bits, u32 mask) +{ + unsigned int i; + + printk(KERN_DEBUG "%s: %s [0x%x]", name, tag, bits); + + for (i = 0; i < len; i++) { + if (!(bits & (1 << i))) + continue; + if (strings[i]) + printk(" %s", strings[i]); + else + printk(" %d", i); + if (!(mask & (1 << i))) + continue; + printk("*"); + } + printk("\n"); +} + +struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci) +{ + struct cx25821_dev *dev = pci_get_drvdata(pci); + return dev; +} + +static int __devinit cx25821_initdev(struct pci_dev *pci_dev, + const struct pci_device_id *pci_id) +{ + struct cx25821_dev *dev; + int err = 0; + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (NULL == dev) + return -ENOMEM; + + err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev); + if (err < 0) + goto fail_free; + + /* pci init */ + dev->pci = pci_dev; + if (pci_enable_device(pci_dev)) { + err = -EIO; + + printk(KERN_INFO "pci enable failed! "); + + goto fail_unregister_device; + } + + printk(KERN_INFO "cx25821 Athena pci enable ! \n"); + + if (cx25821_dev_setup(dev) < 0) { + err = -EINVAL; + goto fail_unregister_device; + } + + /* print pci info */ + pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev); + pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat); + printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, " + "latency: %d, mmio: 0x%llx\n", dev->name, + pci_name(pci_dev), dev->pci_rev, pci_dev->irq, + dev->pci_lat, (unsigned long long)dev->base_io_addr); + + pci_set_master(pci_dev); + if (!pci_dma_supported(pci_dev, 0xffffffff)) { + printk("%s/0: Oops: no 32bit PCI DMA ???\n", dev->name); + err = -EIO; + goto fail_irq; + } + + err = + request_irq(pci_dev->irq, cx25821_irq, IRQF_SHARED | IRQF_DISABLED, + dev->name, dev); + + if (err < 0) { + printk(KERN_ERR "%s: can't get IRQ %d\n", dev->name, + pci_dev->irq); + goto fail_irq; + } + + return 0; + + fail_irq: + printk(KERN_INFO "cx25821 cx25821_initdev() can't get IRQ ! \n"); + cx25821_dev_unregister(dev); + + fail_unregister_device: + v4l2_device_unregister(&dev->v4l2_dev); + + fail_free: + kfree(dev); + return err; +} + +static void __devexit cx25821_finidev(struct pci_dev *pci_dev) +{ + struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev); + struct cx25821_dev *dev = get_cx25821(v4l2_dev); + + cx25821_shutdown(dev); + pci_disable_device(pci_dev); + + /* unregister stuff */ + if (pci_dev->irq) + free_irq(pci_dev->irq, dev); + + mutex_lock(&devlist); + list_del(&dev->devlist); + mutex_unlock(&devlist); + + cx25821_dev_unregister(dev); + v4l2_device_unregister(v4l2_dev); + kfree(dev); +} + +static struct pci_device_id cx25821_pci_tbl[] = { + { + /* CX25821 Athena */ + .vendor = 0x14f1, + .device = 0x8210, + .subvendor = 0x14f1, + .subdevice = 0x0920, + }, + { + /* --- end of list --- */ + } +}; + +MODULE_DEVICE_TABLE(pci, cx25821_pci_tbl); + +static struct pci_driver cx25821_pci_driver = { + .name = "cx25821", + .id_table = cx25821_pci_tbl, + .probe = cx25821_initdev, + .remove = __devexit_p(cx25821_finidev), + /* TODO */ + .suspend = NULL, + .resume = NULL, +}; + +static int cx25821_init(void) +{ + INIT_LIST_HEAD(&cx25821_devlist); + printk(KERN_INFO "cx25821 driver version %d.%d.%d loaded\n", + (CX25821_VERSION_CODE >> 16) & 0xff, + (CX25821_VERSION_CODE >> 8) & 0xff, CX25821_VERSION_CODE & 0xff); + return pci_register_driver(&cx25821_pci_driver); +} + +static void cx25821_fini(void) +{ + pci_unregister_driver(&cx25821_pci_driver); +} + +EXPORT_SYMBOL(cx25821_devlist); +EXPORT_SYMBOL(cx25821_sram_channels); +EXPORT_SYMBOL(cx25821_print_irqbits); +EXPORT_SYMBOL(cx25821_dev_get); +EXPORT_SYMBOL(cx25821_dev_unregister); +EXPORT_SYMBOL(cx25821_sram_channel_setup); +EXPORT_SYMBOL(cx25821_sram_channel_dump); +EXPORT_SYMBOL(cx25821_sram_channel_setup_audio); +EXPORT_SYMBOL(cx25821_sram_channel_dump_audio); +EXPORT_SYMBOL(cx25821_risc_databuffer_audio); +EXPORT_SYMBOL(cx25821_set_gpiopin_direction); + +module_init(cx25821_init); +module_exit(cx25821_fini); diff --git a/linux/drivers/staging/cx25821/cx25821-gpio.c b/linux/drivers/staging/cx25821/cx25821-gpio.c new file mode 100644 index 000000000..e8a37b47e --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-gpio.c @@ -0,0 +1,98 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821.h" + +/********************* GPIO stuffs *********************/ +void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, + int pin_number, int pin_logic_value) +{ + int bit = pin_number; + u32 gpio_oe_reg = GPIO_LO_OE; + u32 gpio_register = 0; + u32 value = 0; + + // Check for valid pinNumber + if (pin_number >= 47) + return; + + if (pin_number > 31) { + bit = pin_number - 31; + gpio_oe_reg = GPIO_HI_OE; + } + // Here we will make sure that the GPIOs 0 and 1 are output. keep the rest as is + gpio_register = cx_read(gpio_oe_reg); + + if (pin_logic_value == 1) { + value = gpio_register | Set_GPIO_Bit(bit); + } else { + value = gpio_register & Clear_GPIO_Bit(bit); + } + + cx_write(gpio_oe_reg, value); +} + +static void cx25821_set_gpiopin_logicvalue(struct cx25821_dev *dev, + int pin_number, int pin_logic_value) +{ + int bit = pin_number; + u32 gpio_reg = GPIO_LO; + u32 value = 0; + + // Check for valid pinNumber + if (pin_number >= 47) + return; + + cx25821_set_gpiopin_direction(dev, pin_number, 0); // change to output direction + + if (pin_number > 31) { + bit = pin_number - 31; + gpio_reg = GPIO_HI; + } + + value = cx_read(gpio_reg); + + if (pin_logic_value == 0) { + value &= Clear_GPIO_Bit(bit); + } else { + value |= Set_GPIO_Bit(bit); + } + + cx_write(gpio_reg, value); +} + +void cx25821_gpio_init(struct cx25821_dev *dev) +{ + if (dev == NULL) { + return; + } + + switch (dev->board) { + case CX25821_BOARD_CONEXANT_ATHENA10: + default: + //set GPIO 5 to select the path for Medusa/Athena + cx25821_set_gpiopin_logicvalue(dev, 5, 1); + mdelay(20); + break; + } + +} diff --git a/linux/drivers/staging/cx25821/cx25821-gpio.h b/linux/drivers/staging/cx25821/cx25821-gpio.h new file mode 100644 index 000000000..ca0764415 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-gpio.h @@ -0,0 +1,2 @@ + +void cx25821_gpio_init(struct athena_dev *dev); diff --git a/linux/drivers/staging/cx25821/cx25821-i2c.c b/linux/drivers/staging/cx25821/cx25821-i2c.c new file mode 100644 index 000000000..0769f9519 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-i2c.c @@ -0,0 +1,467 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821.h" +#include <linux/i2c.h> + +static unsigned int i2c_debug; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +static unsigned int i2c_scan = 0; +module_param(i2c_scan, int, 0444); +MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time"); + +#define dprintk(level, fmt, arg...)\ + do { if (i2c_debug >= level)\ + printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ + } while (0) + +#define I2C_WAIT_DELAY 32 +#define I2C_WAIT_RETRY 64 + +#define I2C_EXTEND (1 << 3) +#define I2C_NOSTOP (1 << 4) + +static inline int i2c_slave_did_ack(struct i2c_adapter *i2c_adap) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + return cx_read(bus->reg_stat) & 0x01; +} + +static inline int i2c_is_busy(struct i2c_adapter *i2c_adap) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + return cx_read(bus->reg_stat) & 0x02 ? 1 : 0; +} + +static int i2c_wait_done(struct i2c_adapter *i2c_adap) +{ + int count; + + for (count = 0; count < I2C_WAIT_RETRY; count++) { + if (!i2c_is_busy(i2c_adap)) + break; + udelay(I2C_WAIT_DELAY); + } + + if (I2C_WAIT_RETRY == count) + return 0; + + return 1; +} + +static int i2c_sendbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined_rlen) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + u32 wdata, addr, ctrl; + int retval, cnt; + + if (joined_rlen) + dprintk(1, "%s(msg->wlen=%d, nextmsg->rlen=%d)\n", __func__, + msg->len, joined_rlen); + else + dprintk(1, "%s(msg->len=%d)\n", __func__, msg->len); + + /* Deal with i2c probe functions with zero payload */ + if (msg->len == 0) { + cx_write(bus->reg_addr, msg->addr << 25); + cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2)); + + if (!i2c_wait_done(i2c_adap)) + return -EIO; + + if (!i2c_slave_did_ack(i2c_adap)) + return -EIO; + + dprintk(1, "%s() returns 0\n", __func__); + return 0; + } + + /* dev, reg + first byte */ + addr = (msg->addr << 25) | msg->buf[0]; + wdata = msg->buf[0]; + + ctrl = bus->i2c_period | (1 << 12) | (1 << 2); + + if (msg->len > 1) + ctrl |= I2C_NOSTOP | I2C_EXTEND; + else if (joined_rlen) + ctrl |= I2C_NOSTOP; + + cx_write(bus->reg_addr, addr); + cx_write(bus->reg_wdata, wdata); + cx_write(bus->reg_ctrl, ctrl); + + retval = i2c_wait_done(i2c_adap); + if (retval < 0) + goto err; + + if (retval == 0) + goto eio; + + if (i2c_debug) { + if (!(ctrl & I2C_NOSTOP)) + printk(" >\n"); + } + + for (cnt = 1; cnt < msg->len; cnt++) { + /* following bytes */ + wdata = msg->buf[cnt]; + ctrl = bus->i2c_period | (1 << 12) | (1 << 2); + + if (cnt < msg->len - 1) + ctrl |= I2C_NOSTOP | I2C_EXTEND; + else if (joined_rlen) + ctrl |= I2C_NOSTOP; + + cx_write(bus->reg_addr, addr); + cx_write(bus->reg_wdata, wdata); + cx_write(bus->reg_ctrl, ctrl); + + retval = i2c_wait_done(i2c_adap); + if (retval < 0) + goto err; + + if (retval == 0) + goto eio; + + if (i2c_debug) { + dprintk(1, " %02x", msg->buf[cnt]); + if (!(ctrl & I2C_NOSTOP)) + dprintk(1, " >\n"); + } + } + + return msg->len; + + eio: + retval = -EIO; + err: + if (i2c_debug) + printk(KERN_ERR " ERR: %d\n", retval); + return retval; +} + +static int i2c_readbytes(struct i2c_adapter *i2c_adap, + const struct i2c_msg *msg, int joined) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + u32 ctrl, cnt; + int retval; + + if (i2c_debug && !joined) + dprintk(1, "6-%s(msg->len=%d)\n", __func__, msg->len); + + /* Deal with i2c probe functions with zero payload */ + if (msg->len == 0) { + cx_write(bus->reg_addr, msg->addr << 25); + cx_write(bus->reg_ctrl, bus->i2c_period | (1 << 2) | 1); + if (!i2c_wait_done(i2c_adap)) + return -EIO; + if (!i2c_slave_did_ack(i2c_adap)) + return -EIO; + + dprintk(1, "%s() returns 0\n", __func__); + return 0; + } + + if (i2c_debug) { + if (joined) + dprintk(1, " R"); + else + dprintk(1, " <R %02x", (msg->addr << 1) + 1); + } + + for (cnt = 0; cnt < msg->len; cnt++) { + + ctrl = bus->i2c_period | (1 << 12) | (1 << 2) | 1; + + if (cnt < msg->len - 1) + ctrl |= I2C_NOSTOP | I2C_EXTEND; + + cx_write(bus->reg_addr, msg->addr << 25); + cx_write(bus->reg_ctrl, ctrl); + + retval = i2c_wait_done(i2c_adap); + if (retval < 0) + goto err; + if (retval == 0) + goto eio; + msg->buf[cnt] = cx_read(bus->reg_rdata) & 0xff; + + if (i2c_debug) { + dprintk(1, " %02x", msg->buf[cnt]); + if (!(ctrl & I2C_NOSTOP)) + dprintk(1, " >\n"); + } + } + + return msg->len; + eio: + retval = -EIO; + err: + if (i2c_debug) + printk(KERN_ERR " ERR: %d\n", retval); + return retval; +} + +static int i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num) +{ + struct cx25821_i2c *bus = i2c_adap->algo_data; + struct cx25821_dev *dev = bus->dev; + int i, retval = 0; + + dprintk(1, "%s(num = %d)\n", __func__, num); + + for (i = 0; i < num; i++) { + dprintk(1, "%s(num = %d) addr = 0x%02x len = 0x%x\n", + __func__, num, msgs[i].addr, msgs[i].len); + + if (msgs[i].flags & I2C_M_RD) { + /* read */ + retval = i2c_readbytes(i2c_adap, &msgs[i], 0); + } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* write then read from same address */ + retval = + i2c_sendbytes(i2c_adap, &msgs[i], msgs[i + 1].len); + + if (retval < 0) + goto err; + i++; + retval = i2c_readbytes(i2c_adap, &msgs[i], 1); + } else { + /* write */ + retval = i2c_sendbytes(i2c_adap, &msgs[i], 0); + } + + if (retval < 0) + goto err; + } + return num; + + err: + return retval; +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) +static int attach_inform(struct i2c_client *client) +{ + struct cx25821_i2c *bus = i2c_get_adapdata(client->adapter); + struct cx25821_dev *dev = bus->dev; + + printk(KERN_INFO "attach: %s i2c attach [addr=0x%x,client=%s]\n", + client->driver->driver.name, client->addr, client->name); + + if (!client->driver->command) + return 0; + + if (dev->videc_type != UNSET) { + + printk(KERN_INFO "%s i2c attach [addr=0x%x,client=%s]\n", + client->driver->driver.name, client->addr, client->name); + + } + + printk(KERN_INFO "attach done!\n"); + + return 0; +} + +static int detach_inform(struct i2c_client *client) +{ + struct cx25821_dev *dev = i2c_get_adapdata(client->adapter); + + dprintk(1, "i2c detach [client=%s]\n", client->name); + + return 0; +} + +void cx25821_call_i2c_clients(struct cx25821_i2c *bus, unsigned int cmd, + void *arg) +{ + if (bus->i2c_rc != 0) + return; + + i2c_clients_command(&bus->i2c_adap, cmd, arg); +} +#endif + +static u32 cx25821_functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL | + I2C_FUNC_I2C | + I2C_FUNC_SMBUS_WORD_DATA | + I2C_FUNC_SMBUS_READ_WORD_DATA | I2C_FUNC_SMBUS_WRITE_WORD_DATA; +} + +static struct i2c_algorithm cx25821_i2c_algo_template = { + .master_xfer = i2c_xfer, + .functionality = cx25821_functionality, +#ifdef NEED_ALGO_CONTROL + .algo_control = dummy_algo_control, +#endif +}; + +static struct i2c_adapter cx25821_i2c_adap_template = { + .name = "cx25821", + .owner = THIS_MODULE, + .algo = &cx25821_i2c_algo_template, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 26) + .class = I2C_CLASS_TV_ANALOG, +#endif +}; + +static struct i2c_client cx25821_i2c_client_template = { + .name = "cx25821 internal", +}; + +/* init + register i2c algo-bit adapter */ +int cx25821_i2c_register(struct cx25821_i2c *bus) +{ + struct cx25821_dev *dev = bus->dev; + + dprintk(1, "%s(bus = %d)\n", __func__, bus->nr); + + memcpy(&bus->i2c_adap, &cx25821_i2c_adap_template, + sizeof(bus->i2c_adap)); + memcpy(&bus->i2c_algo, &cx25821_i2c_algo_template, + sizeof(bus->i2c_algo)); + memcpy(&bus->i2c_client, &cx25821_i2c_client_template, + sizeof(bus->i2c_client)); + + bus->i2c_adap.dev.parent = &dev->pci->dev; + + strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name)); + + bus->i2c_algo.data = bus; + bus->i2c_adap.algo_data = bus; + i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev); + i2c_add_adapter(&bus->i2c_adap); + + bus->i2c_client.adapter = &bus->i2c_adap; + + //set up the I2c + bus->i2c_client.addr = (0x88 >> 1); + + return bus->i2c_rc; +} + +int cx25821_i2c_unregister(struct cx25821_i2c *bus) +{ + i2c_del_adapter(&bus->i2c_adap); + return 0; +} + +void cx25821_av_clk(struct cx25821_dev *dev, int enable) +{ + /* write 0 to bus 2 addr 0x144 via i2x_xfer() */ + char buffer[3]; + struct i2c_msg msg; + dprintk(1, "%s(enabled = %d)\n", __func__, enable); + + /* Register 0x144 */ + buffer[0] = 0x01; + buffer[1] = 0x44; + if (enable == 1) + buffer[2] = 0x05; + else + buffer[2] = 0x00; + + msg.addr = 0x44; + msg.flags = I2C_M_TEN; + msg.len = 3; + msg.buf = buffer; + + i2c_xfer(&dev->i2c_bus[0].i2c_adap, &msg, 1); +} + +int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value) +{ + struct i2c_client *client = &bus->i2c_client; + int retval = 0; + int v = 0; + u8 addr[2] = { 0, 0 }; + u8 buf[4] = { 0, 0, 0, 0 }; + + struct i2c_msg msgs[2] = { + { + .addr = client->addr, + .flags = 0, + .len = 2, + .buf = addr, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 4, + .buf = buf, + } + }; + + addr[0] = (reg_addr >> 8); + addr[1] = (reg_addr & 0xff); + msgs[0].addr = 0x44; + msgs[1].addr = 0x44; + + retval = i2c_xfer(client->adapter, msgs, 2); + + v = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; + *value = v; + + return v; +} + +int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value) +{ + struct i2c_client *client = &bus->i2c_client; + int retval = 0; + u8 buf[6] = { 0, 0, 0, 0, 0, 0 }; + + struct i2c_msg msgs[1] = { + { + .addr = client->addr, + .flags = 0, + .len = 6, + .buf = buf, + } + }; + + buf[0] = reg_addr >> 8; + buf[1] = reg_addr & 0xff; + buf[5] = (value >> 24) & 0xff; + buf[4] = (value >> 16) & 0xff; + buf[3] = (value >> 8) & 0xff; + buf[2] = value & 0xff; + client->flags = 0; + msgs[0].addr = 0x44; + + retval = i2c_xfer(client->adapter, msgs, 1); + + return retval; +} diff --git a/linux/drivers/staging/cx25821/cx25821-medusa-defines.h b/linux/drivers/staging/cx25821/cx25821-medusa-defines.h new file mode 100644 index 000000000..b0d216ba7 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-medusa-defines.h @@ -0,0 +1,51 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEDUSA_DEF_H_ +#define _MEDUSA_DEF_H_ + +// Video deocder that we supported +#define VDEC_A 0 +#define VDEC_B 1 +#define VDEC_C 2 +#define VDEC_D 3 +#define VDEC_E 4 +#define VDEC_F 5 +#define VDEC_G 6 +#define VDEC_H 7 + +//#define AUTO_SWITCH_BIT[] = { 8, 9, 10, 11, 12, 13, 14, 15 }; + +// The following bit position enables automatic source switching for decoder A-H. +// Display index per camera. +//#define VDEC_INDEX[] = {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7}; + +// Select input bit to video decoder A-H. +//#define CH_SRC_SEL_BIT[] = {24, 25, 26, 27, 28, 29, 30, 31}; + +// end of display sequence +#define END_OF_SEQ 0xF; + +// registry string size +#define MAX_REGISTRY_SZ 40; + +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-medusa-reg.h b/linux/drivers/staging/cx25821/cx25821-medusa-reg.h new file mode 100644 index 000000000..12c90f831 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-medusa-reg.h @@ -0,0 +1,455 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MEDUSA_REGISTERS__ +#define __MEDUSA_REGISTERS__ + +// Serial Slave Registers +#define HOST_REGISTER1 0x0000 +#define HOST_REGISTER2 0x0001 + +// Chip Configuration Registers +#define CHIP_CTRL 0x0100 +#define AFE_AB_CTRL 0x0104 +#define AFE_CD_CTRL 0x0108 +#define AFE_EF_CTRL 0x010C +#define AFE_GH_CTRL 0x0110 +#define DENC_AB_CTRL 0x0114 +#define BYP_AB_CTRL 0x0118 +#define MON_A_CTRL 0x011C +#define DISP_SEQ_A 0x0120 +#define DISP_SEQ_B 0x0124 +#define DISP_AB_CNT 0x0128 +#define DISP_CD_CNT 0x012C +#define DISP_EF_CNT 0x0130 +#define DISP_GH_CNT 0x0134 +#define DISP_IJ_CNT 0x0138 +#define PIN_OE_CTRL 0x013C +#define PIN_SPD_CTRL 0x0140 +#define PIN_SPD_CTRL2 0x0144 +#define IRQ_STAT_CTRL 0x0148 +#define POWER_CTRL_AB 0x014C +#define POWER_CTRL_CD 0x0150 +#define POWER_CTRL_EF 0x0154 +#define POWER_CTRL_GH 0x0158 +#define TUNE_CTRL 0x015C +#define BIAS_CTRL 0x0160 +#define AFE_AB_DIAG_CTRL 0x0164 +#define AFE_CD_DIAG_CTRL 0x0168 +#define AFE_EF_DIAG_CTRL 0x016C +#define AFE_GH_DIAG_CTRL 0x0170 +#define PLL_AB_DIAG_CTRL 0x0174 +#define PLL_CD_DIAG_CTRL 0x0178 +#define PLL_EF_DIAG_CTRL 0x017C +#define PLL_GH_DIAG_CTRL 0x0180 +#define TEST_CTRL 0x0184 +#define BIST_STAT 0x0188 +#define BIST_STAT2 0x018C +#define BIST_VID_PLL_AB_STAT 0x0190 +#define BIST_VID_PLL_CD_STAT 0x0194 +#define BIST_VID_PLL_EF_STAT 0x0198 +#define BIST_VID_PLL_GH_STAT 0x019C +#define DLL_DIAG_CTRL 0x01A0 +#define DEV_CH_ID_CTRL 0x01A4 +#define ABIST_CTRL_STATUS 0x01A8 +#define ABIST_FREQ 0x01AC +#define ABIST_GOERT_SHIFT 0x01B0 +#define ABIST_COEF12 0x01B4 +#define ABIST_COEF34 0x01B8 +#define ABIST_COEF56 0x01BC +#define ABIST_COEF7_SNR 0x01C0 +#define ABIST_ADC_CAL 0x01C4 +#define ABIST_BIN1_VGA0 0x01C8 +#define ABIST_BIN2_VGA1 0x01CC +#define ABIST_BIN3_VGA2 0x01D0 +#define ABIST_BIN4_VGA3 0x01D4 +#define ABIST_BIN5_VGA4 0x01D8 +#define ABIST_BIN6_VGA5 0x01DC +#define ABIST_BIN7_VGA6 0x0x1E0 +#define ABIST_CLAMP_A 0x0x1E4 +#define ABIST_CLAMP_B 0x0x1E8 +#define ABIST_CLAMP_C 0x01EC +#define ABIST_CLAMP_D 0x01F0 +#define ABIST_CLAMP_E 0x01F4 +#define ABIST_CLAMP_F 0x01F8 + +// Digital Video Encoder A Registers +#define DENC_A_REG_1 0x0200 +#define DENC_A_REG_2 0x0204 +#define DENC_A_REG_3 0x0208 +#define DENC_A_REG_4 0x020C +#define DENC_A_REG_5 0x0210 +#define DENC_A_REG_6 0x0214 +#define DENC_A_REG_7 0x0218 +#define DENC_A_REG_8 0x021C + +// Digital Video Encoder B Registers +#define DENC_B_REG_1 0x0300 +#define DENC_B_REG_2 0x0304 +#define DENC_B_REG_3 0x0308 +#define DENC_B_REG_4 0x030C +#define DENC_B_REG_5 0x0310 +#define DENC_B_REG_6 0x0314 +#define DENC_B_REG_7 0x0318 +#define DENC_B_REG_8 0x031C + +// Video Decoder A Registers +#define MODE_CTRL 0x1000 +#define OUT_CTRL1 0x1004 +#define OUT_CTRL_NS 0x1008 +#define GEN_STAT 0x100C +#define INT_STAT_MASK 0x1010 +#define LUMA_CTRL 0x1014 +#define CHROMA_CTRL 0x1018 +#define CRUSH_CTRL 0x101C +#define HORIZ_TIM_CTRL 0x1020 +#define VERT_TIM_CTRL 0x1024 +#define MISC_TIM_CTRL 0x1028 +#define FIELD_COUNT 0x102C +#define HSCALE_CTRL 0x1030 +#define VSCALE_CTRL 0x1034 +#define MAN_VGA_CTRL 0x1038 +#define MAN_AGC_CTRL 0x103C +#define DFE_CTRL1 0x1040 +#define DFE_CTRL2 0x1044 +#define DFE_CTRL3 0x1048 +#define PLL_CTRL 0x104C +#define PLL_CTRL_FAST 0x1050 +#define HTL_CTRL 0x1054 +#define SRC_CFG 0x1058 +#define SC_STEP_SIZE 0x105C +#define SC_CONVERGE_CTRL 0x1060 +#define SC_LOOP_CTRL 0x1064 +#define COMB_2D_HFS_CFG 0x1068 +#define COMB_2D_HFD_CFG 0x106C +#define COMB_2D_LF_CFG 0x1070 +#define COMB_2D_BLEND 0x1074 +#define COMB_MISC_CTRL 0x1078 +#define COMB_FLAT_THRESH_CTRL 0x107C +#define COMB_TEST 0x1080 +#define BP_MISC_CTRL 0x1084 +#define VCR_DET_CTRL 0x1088 +#define NOISE_DET_CTRL 0x108C +#define COMB_FLAT_NOISE_CTRL 0x1090 +#define VERSION 0x11F8 +#define SOFT_RST_CTRL 0x11FC + +// Video Decoder B Registers +#define VDEC_B_MODE_CTRL 0x1200 +#define VDEC_B_OUT_CTRL1 0x1204 +#define VDEC_B_OUT_CTRL_NS 0x1208 +#define VDEC_B_GEN_STAT 0x120C +#define VDEC_B_INT_STAT_MASK 0x1210 +#define VDEC_B_LUMA_CTRL 0x1214 +#define VDEC_B_CHROMA_CTRL 0x1218 +#define VDEC_B_CRUSH_CTRL 0x121C +#define VDEC_B_HORIZ_TIM_CTRL 0x1220 +#define VDEC_B_VERT_TIM_CTRL 0x1224 +#define VDEC_B_MISC_TIM_CTRL 0x1228 +#define VDEC_B_FIELD_COUNT 0x122C +#define VDEC_B_HSCALE_CTRL 0x1230 +#define VDEC_B_VSCALE_CTRL 0x1234 +#define VDEC_B_MAN_VGA_CTRL 0x1238 +#define VDEC_B_MAN_AGC_CTRL 0x123C +#define VDEC_B_DFE_CTRL1 0x1240 +#define VDEC_B_DFE_CTRL2 0x1244 +#define VDEC_B_DFE_CTRL3 0x1248 +#define VDEC_B_PLL_CTRL 0x124C +#define VDEC_B_PLL_CTRL_FAST 0x1250 +#define VDEC_B_HTL_CTRL 0x1254 +#define VDEC_B_SRC_CFG 0x1258 +#define VDEC_B_SC_STEP_SIZE 0x125C +#define VDEC_B_SC_CONVERGE_CTRL 0x1260 +#define VDEC_B_SC_LOOP_CTRL 0x1264 +#define VDEC_B_COMB_2D_HFS_CFG 0x1268 +#define VDEC_B_COMB_2D_HFD_CFG 0x126C +#define VDEC_B_COMB_2D_LF_CFG 0x1270 +#define VDEC_B_COMB_2D_BLEND 0x1274 +#define VDEC_B_COMB_MISC_CTRL 0x1278 +#define VDEC_B_COMB_FLAT_THRESH_CTRL 0x127C +#define VDEC_B_COMB_TEST 0x1280 +#define VDEC_B_BP_MISC_CTRL 0x1284 +#define VDEC_B_VCR_DET_CTRL 0x1288 +#define VDEC_B_NOISE_DET_CTRL 0x128C +#define VDEC_B_COMB_FLAT_NOISE_CTRL 0x1290 +#define VDEC_B_VERSION 0x13F8 +#define VDEC_B_SOFT_RST_CTRL 0x13FC + +// Video Decoder C Registers +#define VDEC_C_MODE_CTRL 0x1400 +#define VDEC_C_OUT_CTRL1 0x1404 +#define VDEC_C_OUT_CTRL_NS 0x1408 +#define VDEC_C_GEN_STAT 0x140C +#define VDEC_C_INT_STAT_MASK 0x1410 +#define VDEC_C_LUMA_CTRL 0x1414 +#define VDEC_C_CHROMA_CTRL 0x1418 +#define VDEC_C_CRUSH_CTRL 0x141C +#define VDEC_C_HORIZ_TIM_CTRL 0x1420 +#define VDEC_C_VERT_TIM_CTRL 0x1424 +#define VDEC_C_MISC_TIM_CTRL 0x1428 +#define VDEC_C_FIELD_COUNT 0x142C +#define VDEC_C_HSCALE_CTRL 0x1430 +#define VDEC_C_VSCALE_CTRL 0x1434 +#define VDEC_C_MAN_VGA_CTRL 0x1438 +#define VDEC_C_MAN_AGC_CTRL 0x143C +#define VDEC_C_DFE_CTRL1 0x1440 +#define VDEC_C_DFE_CTRL2 0x1444 +#define VDEC_C_DFE_CTRL3 0x1448 +#define VDEC_C_PLL_CTRL 0x144C +#define VDEC_C_PLL_CTRL_FAST 0x1450 +#define VDEC_C_HTL_CTRL 0x1454 +#define VDEC_C_SRC_CFG 0x1458 +#define VDEC_C_SC_STEP_SIZE 0x145C +#define VDEC_C_SC_CONVERGE_CTRL 0x1460 +#define VDEC_C_SC_LOOP_CTRL 0x1464 +#define VDEC_C_COMB_2D_HFS_CFG 0x1468 +#define VDEC_C_COMB_2D_HFD_CFG 0x146C +#define VDEC_C_COMB_2D_LF_CFG 0x1470 +#define VDEC_C_COMB_2D_BLEND 0x1474 +#define VDEC_C_COMB_MISC_CTRL 0x1478 +#define VDEC_C_COMB_FLAT_THRESH_CTRL 0x147C +#define VDEC_C_COMB_TEST 0x1480 +#define VDEC_C_BP_MISC_CTRL 0x1484 +#define VDEC_C_VCR_DET_CTRL 0x1488 +#define VDEC_C_NOISE_DET_CTRL 0x148C +#define VDEC_C_COMB_FLAT_NOISE_CTRL 0x1490 +#define VDEC_C_VERSION 0x15F8 +#define VDEC_C_SOFT_RST_CTRL 0x15FC + +// Video Decoder D Registers +#define VDEC_D_MODE_CTRL 0x1600 +#define VDEC_D_OUT_CTRL1 0x1604 +#define VDEC_D_OUT_CTRL_NS 0x1608 +#define VDEC_D_GEN_STAT 0x160C +#define VDEC_D_INT_STAT_MASK 0x1610 +#define VDEC_D_LUMA_CTRL 0x1614 +#define VDEC_D_CHROMA_CTRL 0x1618 +#define VDEC_D_CRUSH_CTRL 0x161C +#define VDEC_D_HORIZ_TIM_CTRL 0x1620 +#define VDEC_D_VERT_TIM_CTRL 0x1624 +#define VDEC_D_MISC_TIM_CTRL 0x1628 +#define VDEC_D_FIELD_COUNT 0x162C +#define VDEC_D_HSCALE_CTRL 0x1630 +#define VDEC_D_VSCALE_CTRL 0x1634 +#define VDEC_D_MAN_VGA_CTRL 0x1638 +#define VDEC_D_MAN_AGC_CTRL 0x163C +#define VDEC_D_DFE_CTRL1 0x1640 +#define VDEC_D_DFE_CTRL2 0x1644 +#define VDEC_D_DFE_CTRL3 0x1648 +#define VDEC_D_PLL_CTRL 0x164C +#define VDEC_D_PLL_CTRL_FAST 0x1650 +#define VDEC_D_HTL_CTRL 0x1654 +#define VDEC_D_SRC_CFG 0x1658 +#define VDEC_D_SC_STEP_SIZE 0x165C +#define VDEC_D_SC_CONVERGE_CTRL 0x1660 +#define VDEC_D_SC_LOOP_CTRL 0x1664 +#define VDEC_D_COMB_2D_HFS_CFG 0x1668 +#define VDEC_D_COMB_2D_HFD_CFG 0x166C +#define VDEC_D_COMB_2D_LF_CFG 0x1670 +#define VDEC_D_COMB_2D_BLEND 0x1674 +#define VDEC_D_COMB_MISC_CTRL 0x1678 +#define VDEC_D_COMB_FLAT_THRESH_CTRL 0x167C +#define VDEC_D_COMB_TEST 0x1680 +#define VDEC_D_BP_MISC_CTRL 0x1684 +#define VDEC_D_VCR_DET_CTRL 0x1688 +#define VDEC_D_NOISE_DET_CTRL 0x168C +#define VDEC_D_COMB_FLAT_NOISE_CTRL 0x1690 +#define VDEC_D_VERSION 0x17F8 +#define VDEC_D_SOFT_RST_CTRL 0x17FC + +// Video Decoder E Registers +#define VDEC_E_MODE_CTRL 0x1800 +#define VDEC_E_OUT_CTRL1 0x1804 +#define VDEC_E_OUT_CTRL_NS 0x1808 +#define VDEC_E_GEN_STAT 0x180C +#define VDEC_E_INT_STAT_MASK 0x1810 +#define VDEC_E_LUMA_CTRL 0x1814 +#define VDEC_E_CHROMA_CTRL 0x1818 +#define VDEC_E_CRUSH_CTRL 0x181C +#define VDEC_E_HORIZ_TIM_CTRL 0x1820 +#define VDEC_E_VERT_TIM_CTRL 0x1824 +#define VDEC_E_MISC_TIM_CTRL 0x1828 +#define VDEC_E_FIELD_COUNT 0x182C +#define VDEC_E_HSCALE_CTRL 0x1830 +#define VDEC_E_VSCALE_CTRL 0x1834 +#define VDEC_E_MAN_VGA_CTRL 0x1838 +#define VDEC_E_MAN_AGC_CTRL 0x183C +#define VDEC_E_DFE_CTRL1 0x1840 +#define VDEC_E_DFE_CTRL2 0x1844 +#define VDEC_E_DFE_CTRL3 0x1848 +#define VDEC_E_PLL_CTRL 0x184C +#define VDEC_E_PLL_CTRL_FAST 0x1850 +#define VDEC_E_HTL_CTRL 0x1854 +#define VDEC_E_SRC_CFG 0x1858 +#define VDEC_E_SC_STEP_SIZE 0x185C +#define VDEC_E_SC_CONVERGE_CTRL 0x1860 +#define VDEC_E_SC_LOOP_CTRL 0x1864 +#define VDEC_E_COMB_2D_HFS_CFG 0x1868 +#define VDEC_E_COMB_2D_HFD_CFG 0x186C +#define VDEC_E_COMB_2D_LF_CFG 0x1870 +#define VDEC_E_COMB_2D_BLEND 0x1874 +#define VDEC_E_COMB_MISC_CTRL 0x1878 +#define VDEC_E_COMB_FLAT_THRESH_CTRL 0x187C +#define VDEC_E_COMB_TEST 0x1880 +#define VDEC_E_BP_MISC_CTRL 0x1884 +#define VDEC_E_VCR_DET_CTRL 0x1888 +#define VDEC_E_NOISE_DET_CTRL 0x188C +#define VDEC_E_COMB_FLAT_NOISE_CTRL 0x1890 +#define VDEC_E_VERSION 0x19F8 +#define VDEC_E_SOFT_RST_CTRL 0x19FC + +// Video Decoder F Registers +#define VDEC_F_MODE_CTRL 0x1A00 +#define VDEC_F_OUT_CTRL1 0x1A04 +#define VDEC_F_OUT_CTRL_NS 0x1A08 +#define VDEC_F_GEN_STAT 0x1A0C +#define VDEC_F_INT_STAT_MASK 0x1A10 +#define VDEC_F_LUMA_CTRL 0x1A14 +#define VDEC_F_CHROMA_CTRL 0x1A18 +#define VDEC_F_CRUSH_CTRL 0x1A1C +#define VDEC_F_HORIZ_TIM_CTRL 0x1A20 +#define VDEC_F_VERT_TIM_CTRL 0x1A24 +#define VDEC_F_MISC_TIM_CTRL 0x1A28 +#define VDEC_F_FIELD_COUNT 0x1A2C +#define VDEC_F_HSCALE_CTRL 0x1A30 +#define VDEC_F_VSCALE_CTRL 0x1A34 +#define VDEC_F_MAN_VGA_CTRL 0x1A38 +#define VDEC_F_MAN_AGC_CTRL 0x1A3C +#define VDEC_F_DFE_CTRL1 0x1A40 +#define VDEC_F_DFE_CTRL2 0x1A44 +#define VDEC_F_DFE_CTRL3 0x1A48 +#define VDEC_F_PLL_CTRL 0x1A4C +#define VDEC_F_PLL_CTRL_FAST 0x1A50 +#define VDEC_F_HTL_CTRL 0x1A54 +#define VDEC_F_SRC_CFG 0x1A58 +#define VDEC_F_SC_STEP_SIZE 0x1A5C +#define VDEC_F_SC_CONVERGE_CTRL 0x1A60 +#define VDEC_F_SC_LOOP_CTRL 0x1A64 +#define VDEC_F_COMB_2D_HFS_CFG 0x1A68 +#define VDEC_F_COMB_2D_HFD_CFG 0x1A6C +#define VDEC_F_COMB_2D_LF_CFG 0x1A70 +#define VDEC_F_COMB_2D_BLEND 0x1A74 +#define VDEC_F_COMB_MISC_CTRL 0x1A78 +#define VDEC_F_COMB_FLAT_THRESH_CTRL 0x1A7C +#define VDEC_F_COMB_TEST 0x1A80 +#define VDEC_F_BP_MISC_CTRL 0x1A84 +#define VDEC_F_VCR_DET_CTRL 0x1A88 +#define VDEC_F_NOISE_DET_CTRL 0x1A8C +#define VDEC_F_COMB_FLAT_NOISE_CTRL 0x1A90 +#define VDEC_F_VERSION 0x1BF8 +#define VDEC_F_SOFT_RST_CTRL 0x1BFC + +// Video Decoder G Registers +#define VDEC_G_MODE_CTRL 0x1C00 +#define VDEC_G_OUT_CTRL1 0x1C04 +#define VDEC_G_OUT_CTRL_NS 0x1C08 +#define VDEC_G_GEN_STAT 0x1C0C +#define VDEC_G_INT_STAT_MASK 0x1C10 +#define VDEC_G_LUMA_CTRL 0x1C14 +#define VDEC_G_CHROMA_CTRL 0x1C18 +#define VDEC_G_CRUSH_CTRL 0x1C1C +#define VDEC_G_HORIZ_TIM_CTRL 0x1C20 +#define VDEC_G_VERT_TIM_CTRL 0x1C24 +#define VDEC_G_MISC_TIM_CTRL 0x1C28 +#define VDEC_G_FIELD_COUNT 0x1C2C +#define VDEC_G_HSCALE_CTRL 0x1C30 +#define VDEC_G_VSCALE_CTRL 0x1C34 +#define VDEC_G_MAN_VGA_CTRL 0x1C38 +#define VDEC_G_MAN_AGC_CTRL 0x1C3C +#define VDEC_G_DFE_CTRL1 0x1C40 +#define VDEC_G_DFE_CTRL2 0x1C44 +#define VDEC_G_DFE_CTRL3 0x1C48 +#define VDEC_G_PLL_CTRL 0x1C4C +#define VDEC_G_PLL_CTRL_FAST 0x1C50 +#define VDEC_G_HTL_CTRL 0x1C54 +#define VDEC_G_SRC_CFG 0x1C58 +#define VDEC_G_SC_STEP_SIZE 0x1C5C +#define VDEC_G_SC_CONVERGE_CTRL 0x1C60 +#define VDEC_G_SC_LOOP_CTRL 0x1C64 +#define VDEC_G_COMB_2D_HFS_CFG 0x1C68 +#define VDEC_G_COMB_2D_HFD_CFG 0x1C6C +#define VDEC_G_COMB_2D_LF_CFG 0x1C70 +#define VDEC_G_COMB_2D_BLEND 0x1C74 +#define VDEC_G_COMB_MISC_CTRL 0x1C78 +#define VDEC_G_COMB_FLAT_THRESH_CTRL 0x1C7C +#define VDEC_G_COMB_TEST 0x1C80 +#define VDEC_G_BP_MISC_CTRL 0x1C84 +#define VDEC_G_VCR_DET_CTRL 0x1C88 +#define VDEC_G_NOISE_DET_CTRL 0x1C8C +#define VDEC_G_COMB_FLAT_NOISE_CTRL 0x1C90 +#define VDEC_G_VERSION 0x1DF8 +#define VDEC_G_SOFT_RST_CTRL 0x1DFC + +// Video Decoder H Registers +#define VDEC_H_MODE_CTRL 0x1E00 +#define VDEC_H_OUT_CTRL1 0x1E04 +#define VDEC_H_OUT_CTRL_NS 0x1E08 +#define VDEC_H_GEN_STAT 0x1E0C +#define VDEC_H_INT_STAT_MASK 0x1E1E +#define VDEC_H_LUMA_CTRL 0x1E14 +#define VDEC_H_CHROMA_CTRL 0x1E18 +#define VDEC_H_CRUSH_CTRL 0x1E1C +#define VDEC_H_HORIZ_TIM_CTRL 0x1E20 +#define VDEC_H_VERT_TIM_CTRL 0x1E24 +#define VDEC_H_MISC_TIM_CTRL 0x1E28 +#define VDEC_H_FIELD_COUNT 0x1E2C +#define VDEC_H_HSCALE_CTRL 0x1E30 +#define VDEC_H_VSCALE_CTRL 0x1E34 +#define VDEC_H_MAN_VGA_CTRL 0x1E38 +#define VDEC_H_MAN_AGC_CTRL 0x1E3C +#define VDEC_H_DFE_CTRL1 0x1E40 +#define VDEC_H_DFE_CTRL2 0x1E44 +#define VDEC_H_DFE_CTRL3 0x1E48 +#define VDEC_H_PLL_CTRL 0x1E4C +#define VDEC_H_PLL_CTRL_FAST 0x1E50 +#define VDEC_H_HTL_CTRL 0x1E54 +#define VDEC_H_SRC_CFG 0x1E58 +#define VDEC_H_SC_STEP_SIZE 0x1E5C +#define VDEC_H_SC_CONVERGE_CTRL 0x1E60 +#define VDEC_H_SC_LOOP_CTRL 0x1E64 +#define VDEC_H_COMB_2D_HFS_CFG 0x1E68 +#define VDEC_H_COMB_2D_HFD_CFG 0x1E6C +#define VDEC_H_COMB_2D_LF_CFG 0x1E70 +#define VDEC_H_COMB_2D_BLEND 0x1E74 +#define VDEC_H_COMB_MISC_CTRL 0x1E78 +#define VDEC_H_COMB_FLAT_THRESH_CTRL 0x1E7C +#define VDEC_H_COMB_TEST 0x1E80 +#define VDEC_H_BP_MISC_CTRL 0x1E84 +#define VDEC_H_VCR_DET_CTRL 0x1E88 +#define VDEC_H_NOISE_DET_CTRL 0x1E8C +#define VDEC_H_COMB_FLAT_NOISE_CTRL 0x1E90 +#define VDEC_H_VERSION 0x1FF8 +#define VDEC_H_SOFT_RST_CTRL 0x1FFC + +//***************************************************************************** +// LUMA_CTRL register fields +#define VDEC_A_BRITE_CTRL 0x1014 +#define VDEC_A_CNTRST_CTRL 0x1015 +#define VDEC_A_PEAK_SEL 0x1016 + +//***************************************************************************** +// CHROMA_CTRL register fields +#define VDEC_A_USAT_CTRL 0x1018 +#define VDEC_A_VSAT_CTRL 0x1019 +#define VDEC_A_HUE_CTRL 0x101A + +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-medusa-video.c b/linux/drivers/staging/cx25821/cx25821-medusa-video.c new file mode 100644 index 000000000..e4df8134f --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-medusa-video.c @@ -0,0 +1,869 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821.h" +#include "cx25821-medusa-video.h" +#include "cx25821-biffuncs.h" + +///////////////////////////////////////////////////////////////////////////////////////// +//medusa_enable_bluefield_output() +// +// Enable the generation of blue filed output if no video +// +static void medusa_enable_bluefield_output(struct cx25821_dev *dev, int channel, + int enable) +{ + int ret_val = 1; + u32 value = 0; + u32 tmp = 0; + int out_ctrl = OUT_CTRL1; + int out_ctrl_ns = OUT_CTRL_NS; + + switch (channel) { + default: + case VDEC_A: + break; + case VDEC_B: + out_ctrl = VDEC_B_OUT_CTRL1; + out_ctrl_ns = VDEC_B_OUT_CTRL_NS; + break; + case VDEC_C: + out_ctrl = VDEC_C_OUT_CTRL1; + out_ctrl_ns = VDEC_C_OUT_CTRL_NS; + break; + case VDEC_D: + out_ctrl = VDEC_D_OUT_CTRL1; + out_ctrl_ns = VDEC_D_OUT_CTRL_NS; + break; + case VDEC_E: + out_ctrl = VDEC_E_OUT_CTRL1; + out_ctrl_ns = VDEC_E_OUT_CTRL_NS; + return; + case VDEC_F: + out_ctrl = VDEC_F_OUT_CTRL1; + out_ctrl_ns = VDEC_F_OUT_CTRL_NS; + return; + case VDEC_G: + out_ctrl = VDEC_G_OUT_CTRL1; + out_ctrl_ns = VDEC_G_OUT_CTRL_NS; + return; + case VDEC_H: + out_ctrl = VDEC_H_OUT_CTRL1; + out_ctrl_ns = VDEC_H_OUT_CTRL_NS; + return; + } + + value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl, &tmp); + value &= 0xFFFFFF7F; // clear BLUE_FIELD_EN + if (enable) + value |= 0x00000080; // set BLUE_FIELD_EN + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl, value); + + value = cx25821_i2c_read(&dev->i2c_bus[0], out_ctrl_ns, &tmp); + value &= 0xFFFFFF7F; + if (enable) + value |= 0x00000080; // set BLUE_FIELD_EN + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], out_ctrl_ns, value); +} + +static int medusa_initialize_ntsc(struct cx25821_dev *dev) +{ + int ret_val = 0; + int i = 0; + u32 value = 0; + u32 tmp = 0; + + mutex_lock(&dev->lock); + + for (i = 0; i < MAX_DECODERS; i++) { + // set video format NTSC-M + value = + cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + &tmp); + value &= 0xFFFFFFF0; + value |= 0x10001; // enable the fast locking mode bit[16] + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + value); + + // resolution NTSC 720x480 + value = + cx25821_i2c_read(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x612D0074; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x1C1E001A; // vblank_cnt + 2 to get camera ID + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), value); + + // chroma subcarrier step size + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + SC_STEP_SIZE + (0x200 * i), 0x43E00000); + + // enable VIP optional active + value = + cx25821_i2c_read(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), value); + + // enable VIP optional active (VIP_OPT_AL) for direct output. + value = + cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + value); + + // clear VPRES_VERT_EN bit, fixes the chroma run away problem + // when the input switching rate < 16 fields + // + value = + cx25821_i2c_read(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), &tmp); + value = setBitAtPos(value, 14); // disable special play detection + value = clearBitAtPos(value, 15); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), value); + + // set vbi_gate_en to 0 + value = + cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + &tmp); + value = clearBitAtPos(value, 29); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + value); + + // Enable the generation of blue field output if no video + medusa_enable_bluefield_output(dev, i, 1); + } + + for (i = 0; i < MAX_ENCODERS; i++) { + // NTSC hclock + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), &tmp); + value &= 0xF000FC00; + value |= 0x06B402D0; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), value); + + // burst begin and burst end + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), &tmp); + value &= 0xFF000000; + value |= 0x007E9054; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), &tmp); + value &= 0xFC00FE00; + value |= 0x00EC00F0; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), value); + + // set NTSC vblank, no phase alternation, 7.5 IRE pedestal + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), &tmp); + value &= 0x00FCFFFF; + value |= 0x13020000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), &tmp); + value &= 0xFFFF0000; + value |= 0x0000E575; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), value); + + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_6 + (0x100 * i), 0x009A89C1); + + // Subcarrier Increment + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_7 + (0x100 * i), 0x21F07C1F); + } + + //set picture resolutions + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); //0 - 720 + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); //0 - 480 + + // set Bypass input format to NTSC 525 lines + value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); + value |= 0x00080200; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); + + mutex_unlock(&dev->lock); + + return ret_val; +} + +static int medusa_PALCombInit(struct cx25821_dev *dev, int dec) +{ + int ret_val = -1; + u32 value = 0, tmp = 0; + + // Setup for 2D threshold + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_HFS_CFG + (0x200 * dec), + 0x20002861); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_HFD_CFG + (0x200 * dec), + 0x20002861); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_LF_CFG + (0x200 * dec), + 0x200A1023); + + // Setup flat chroma and luma thresholds + value = + cx25821_i2c_read(&dev->i2c_bus[0], + COMB_FLAT_THRESH_CTRL + (0x200 * dec), &tmp); + value &= 0x06230000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + COMB_FLAT_THRESH_CTRL + (0x200 * dec), value); + + // set comb 2D blend + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_2D_BLEND + (0x200 * dec), + 0x210F0F0F); + + // COMB MISC CONTROL + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], COMB_MISC_CTRL + (0x200 * dec), + 0x41120A7F); + + return ret_val; +} + +static int medusa_initialize_pal(struct cx25821_dev *dev) +{ + int ret_val = 0; + int i = 0; + u32 value = 0; + u32 tmp = 0; + + mutex_lock(&dev->lock); + + for (i = 0; i < MAX_DECODERS; i++) { + // set video format PAL-BDGHI + value = + cx25821_i2c_read(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + &tmp); + value &= 0xFFFFFFF0; + value |= 0x10004; // enable the fast locking mode bit[16] + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], MODE_CTRL + (0x200 * i), + value); + + // resolution PAL 720x576 + value = + cx25821_i2c_read(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x632D007D; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + HORIZ_TIM_CTRL + (0x200 * i), value); + + // vblank656_cnt=x26, vactive_cnt=240h, vblank_cnt=x24 + value = + cx25821_i2c_read(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), &tmp); + value &= 0x00C00C00; + value |= 0x28240026; // vblank_cnt + 2 to get camera ID + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + VERT_TIM_CTRL + (0x200 * i), value); + + // chroma subcarrier step size + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + SC_STEP_SIZE + (0x200 * i), 0x5411E2D0); + + // enable VIP optional active + value = + cx25821_i2c_read(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + OUT_CTRL_NS + (0x200 * i), value); + + // enable VIP optional active (VIP_OPT_AL) for direct output. + value = + cx25821_i2c_read(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + &tmp); + value &= 0xFFFBFFFF; + value |= 0x00040000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], OUT_CTRL1 + (0x200 * i), + value); + + // clear VPRES_VERT_EN bit, fixes the chroma run away problem + // when the input switching rate < 16 fields + value = + cx25821_i2c_read(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), &tmp); + value = setBitAtPos(value, 14); // disable special play detection + value = clearBitAtPos(value, 15); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + MISC_TIM_CTRL + (0x200 * i), value); + + // set vbi_gate_en to 0 + value = + cx25821_i2c_read(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + &tmp); + value = clearBitAtPos(value, 29); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], DFE_CTRL1 + (0x200 * i), + value); + + medusa_PALCombInit(dev, i); + + // Enable the generation of blue field output if no video + medusa_enable_bluefield_output(dev, i, 1); + } + + for (i = 0; i < MAX_ENCODERS; i++) { + // PAL hclock + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), &tmp); + value &= 0xF000FC00; + value |= 0x06C002D0; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_1 + (0x100 * i), value); + + // burst begin and burst end + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), &tmp); + value &= 0xFF000000; + value |= 0x007E9754; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_2 + (0x100 * i), value); + + // hblank and vactive + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), &tmp); + value &= 0xFC00FE00; + value |= 0x00FC0120; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_3 + (0x100 * i), value); + + // set PAL vblank, phase alternation, 0 IRE pedestal + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), &tmp); + value &= 0x00FCFFFF; + value |= 0x14010000; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_4 + (0x100 * i), value); + + value = + cx25821_i2c_read(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), &tmp); + value &= 0xFFFF0000; + value |= 0x0000F078; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_5 + (0x100 * i), value); + + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_6 + (0x100 * i), 0x00A493CF); + + // Subcarrier Increment + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + DENC_A_REG_7 + (0x100 * i), 0x2A098ACB); + } + + //set picture resolutions + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], HSCALE_CTRL, 0x0); //0 - 720 + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], VSCALE_CTRL, 0x0); //0 - 576 + + // set Bypass input format to PAL 625 lines + value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); + value &= 0xFFF7FDFF; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); + + mutex_unlock(&dev->lock); + + return ret_val; +} + +int medusa_set_videostandard(struct cx25821_dev *dev) +{ + int status = STATUS_SUCCESS; + u32 value = 0, tmp = 0; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) { + status = medusa_initialize_pal(dev); + } else { + status = medusa_initialize_ntsc(dev); + } + + // Enable DENC_A output + value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_A_REG_4, &tmp); + value = setBitAtPos(value, 4); + status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_A_REG_4, value); + + // Enable DENC_B output + value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_B_REG_4, &tmp); + value = setBitAtPos(value, 4); + status = cx25821_i2c_write(&dev->i2c_bus[0], DENC_B_REG_4, value); + + return status; +} + +void medusa_set_resolution(struct cx25821_dev *dev, int width, + int decoder_select) +{ + int decoder = 0; + int decoder_count = 0; + int ret_val = 0; + u32 hscale = 0x0; + u32 vscale = 0x0; + const int MAX_WIDTH = 720; + + mutex_lock(&dev->lock); + + // validate the width - cannot be negative + if (width > MAX_WIDTH) { + printk + ("cx25821 %s() : width %d > MAX_WIDTH %d ! resetting to MAX_WIDTH \n", + __func__, width, MAX_WIDTH); + width = MAX_WIDTH; + } + + if (decoder_select <= 7 && decoder_select >= 0) { + decoder = decoder_select; + decoder_count = decoder_select + 1; + } else { + decoder = 0; + decoder_count = _num_decoders; + } + + switch (width) { + case 320: + hscale = 0x13E34B; + vscale = 0x0; + break; + + case 352: + hscale = 0x10A273; + vscale = 0x0; + break; + + case 176: + hscale = 0x3115B2; + vscale = 0x1E00; + break; + + case 160: + hscale = 0x378D84; + vscale = 0x1E00; + break; + + default: //720 + hscale = 0x0; + vscale = 0x0; + break; + } + + for (; decoder < decoder_count; decoder++) { + // write scaling values for each decoder + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + HSCALE_CTRL + (0x200 * decoder), hscale); + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], + VSCALE_CTRL + (0x200 * decoder), vscale); + } + + mutex_unlock(&dev->lock); +} + +static void medusa_set_decoderduration(struct cx25821_dev *dev, int decoder, + int duration) +{ + int ret_val = 0; + u32 fld_cnt = 0; + u32 tmp = 0; + u32 disp_cnt_reg = DISP_AB_CNT; + + mutex_lock(&dev->lock); + + // no support + if (decoder < VDEC_A && decoder > VDEC_H) { + mutex_unlock(&dev->lock); + return; + } + + switch (decoder) { + default: + break; + case VDEC_C: + case VDEC_D: + disp_cnt_reg = DISP_CD_CNT; + break; + case VDEC_E: + case VDEC_F: + disp_cnt_reg = DISP_EF_CNT; + break; + case VDEC_G: + case VDEC_H: + disp_cnt_reg = DISP_GH_CNT; + break; + } + + _display_field_cnt[decoder] = duration; + + // update hardware + fld_cnt = cx25821_i2c_read(&dev->i2c_bus[0], disp_cnt_reg, &tmp); + + if (!(decoder % 2)) // EVEN decoder + { + fld_cnt &= 0xFFFF0000; + fld_cnt |= duration; + } else { + fld_cnt &= 0x0000FFFF; + fld_cnt |= ((u32) duration) << 16; + } + + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], disp_cnt_reg, fld_cnt); + + mutex_unlock(&dev->lock); +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Map to Medusa register setting +static int mapM(int srcMin, + int srcMax, int srcVal, int dstMin, int dstMax, int *dstVal) +{ + int numerator; + int denominator; + int quotient; + + if ((srcMin == srcMax) || (srcVal < srcMin) || (srcVal > srcMax)) { + return -1; + } + // This is the overall expression used: + // *dstVal = (srcVal - srcMin)*(dstMax - dstMin) / (srcMax - srcMin) + dstMin; + // but we need to account for rounding so below we use the modulus + // operator to find the remainder and increment if necessary. + numerator = (srcVal - srcMin) * (dstMax - dstMin); + denominator = srcMax - srcMin; + quotient = numerator / denominator; + + if (2 * (numerator % denominator) >= denominator) { + quotient++; + } + + *dstVal = quotient + dstMin; + + return 0; +} + +static unsigned long convert_to_twos(long numeric, unsigned long bits_len) +{ + unsigned char temp; + + if (numeric >= 0) + return numeric; + else { + temp = ~(abs(numeric) & 0xFF); + temp += 1; + return temp; + } +} + +///////////////////////////////////////////////////////////////////////////////////////// +int medusa_set_brightness(struct cx25821_dev *dev, int brightness, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + if ((brightness > VIDEO_PROCAMP_MAX) + || (brightness < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, brightness, + SIGNED_BYTE_MIN, SIGNED_BYTE_MAX, &value); + value = convert_to_twos(value, 8); + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_BRITE_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_BRITE_CTRL + (0x200 * decoder), + val | value); + mutex_unlock(&dev->lock); + return ret_val; +} + +///////////////////////////////////////////////////////////////////////////////////////// +int medusa_set_contrast(struct cx25821_dev *dev, int contrast, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + + if ((contrast > VIDEO_PROCAMP_MAX) || (contrast < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, contrast, + UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value); + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_CNTRST_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_CNTRST_CTRL + (0x200 * decoder), + val | value); + + mutex_unlock(&dev->lock); + return ret_val; +} + +///////////////////////////////////////////////////////////////////////////////////////// +int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + + if ((hue > VIDEO_PROCAMP_MAX) || (hue < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, hue, SIGNED_BYTE_MIN, + SIGNED_BYTE_MAX, &value); + + value = convert_to_twos(value, 8); + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_HUE_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_HUE_CTRL + (0x200 * decoder), val | value); + + mutex_unlock(&dev->lock); + return ret_val; +} + +///////////////////////////////////////////////////////////////////////////////////////// +int medusa_set_saturation(struct cx25821_dev *dev, int saturation, int decoder) +{ + int ret_val = 0; + int value = 0; + u32 val = 0, tmp = 0; + + mutex_lock(&dev->lock); + + if ((saturation > VIDEO_PROCAMP_MAX) + || (saturation < VIDEO_PROCAMP_MIN)) { + mutex_unlock(&dev->lock); + return -1; + } + + ret_val = + mapM(VIDEO_PROCAMP_MIN, VIDEO_PROCAMP_MAX, saturation, + UNSIGNED_BYTE_MIN, UNSIGNED_BYTE_MAX, &value); + + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_USAT_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_USAT_CTRL + (0x200 * decoder), + val | value); + + val = + cx25821_i2c_read(&dev->i2c_bus[0], + VDEC_A_VSAT_CTRL + (0x200 * decoder), &tmp); + val &= 0xFFFFFF00; + ret_val |= + cx25821_i2c_write(&dev->i2c_bus[0], + VDEC_A_VSAT_CTRL + (0x200 * decoder), + val | value); + + mutex_unlock(&dev->lock); + return ret_val; +} + +///////////////////////////////////////////////////////////////////////////////////////// +// Program the display sequence and monitor output. +// +int medusa_video_init(struct cx25821_dev *dev) +{ + u32 value = 0, tmp = 0; + int ret_val = 0; + int i = 0; + + mutex_lock(&dev->lock); + + _num_decoders = dev->_max_num_decoders; + + // disable Auto source selection on all video decoders + value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); + value &= 0xFFFFF0FF; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); + + if (ret_val < 0) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + // Turn off Master source switch enable + value = cx25821_i2c_read(&dev->i2c_bus[0], MON_A_CTRL, &tmp); + value &= 0xFFFFFFDF; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], MON_A_CTRL, value); + + if (ret_val < 0) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + + mutex_unlock(&dev->lock); + + for (i = 0; i < _num_decoders; i++) { + medusa_set_decoderduration(dev, i, _display_field_cnt[i]); + } + + mutex_lock(&dev->lock); + + // Select monitor as DENC A input, power up the DAC + value = cx25821_i2c_read(&dev->i2c_bus[0], DENC_AB_CTRL, &tmp); + value &= 0xFF70FF70; + value |= 0x00090008; // set en_active + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], DENC_AB_CTRL, value); + + if (ret_val < 0) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + // enable input is VIP/656 + value = cx25821_i2c_read(&dev->i2c_bus[0], BYP_AB_CTRL, &tmp); + value |= 0x00040100; // enable VIP + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], BYP_AB_CTRL, value); + + if (ret_val < 0) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + // select AFE clock to output mode + value = cx25821_i2c_read(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, &tmp); + value &= 0x83FFFFFF; + ret_val = + cx25821_i2c_write(&dev->i2c_bus[0], AFE_AB_DIAG_CTRL, + value | 0x10000000); + + if (ret_val < 0) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + // Turn on all of the data out and control output pins. + value = cx25821_i2c_read(&dev->i2c_bus[0], PIN_OE_CTRL, &tmp); + value &= 0xFEF0FE00; + if (_num_decoders == MAX_DECODERS) { + // Note: The octal board does not support control pins(bit16-19). + // These bits are ignored in the octal board. + value |= 0x010001F8; // disable VDEC A-C port, default to Mobilygen Interface + } else { + value |= 0x010F0108; // disable VDEC A-C port, default to Mobilygen Interface + } + + value |= 7; + ret_val = cx25821_i2c_write(&dev->i2c_bus[0], PIN_OE_CTRL, value); + if (ret_val < 0) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + + mutex_unlock(&dev->lock); + + ret_val = medusa_set_videostandard(dev); + + if (ret_val < 0) { + mutex_unlock(&dev->lock); + return -EINVAL; + } + + return 1; +} diff --git a/linux/drivers/staging/cx25821/cx25821-medusa-video.h b/linux/drivers/staging/cx25821/cx25821-medusa-video.h new file mode 100644 index 000000000..2fab4b2f2 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-medusa-video.h @@ -0,0 +1,49 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MEDUSA_VIDEO_H +#define _MEDUSA_VIDEO_H + +#include "cx25821-medusa-defines.h" + +// Color control constants +#define VIDEO_PROCAMP_MIN 0 +#define VIDEO_PROCAMP_MAX 10000 +#define UNSIGNED_BYTE_MIN 0 +#define UNSIGNED_BYTE_MAX 0xFF +#define SIGNED_BYTE_MIN -128 +#define SIGNED_BYTE_MAX 127 + +// Default video color settings +#define SHARPNESS_DEFAULT 50 +#define SATURATION_DEFAULT 5000 +#define BRIGHTNESS_DEFAULT 6200 +#define CONTRAST_DEFAULT 5000 +#define HUE_DEFAULT 5000 + +unsigned short _num_decoders; +unsigned short _num_cameras; + +unsigned int _video_standard; +int _display_field_cnt[MAX_DECODERS]; + +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-reg.h b/linux/drivers/staging/cx25821/cx25821-reg.h new file mode 100644 index 000000000..7241e7ee3 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-reg.h @@ -0,0 +1,1592 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CX25821_REGISTERS__ +#define __CX25821_REGISTERS__ + +/* Risc Instructions */ +#define RISC_CNT_INC 0x00010000 +#define RISC_CNT_RESET 0x00030000 +#define RISC_IRQ1 0x01000000 +#define RISC_IRQ2 0x02000000 +#define RISC_EOL 0x04000000 +#define RISC_SOL 0x08000000 +#define RISC_WRITE 0x10000000 +#define RISC_SKIP 0x20000000 +#define RISC_JUMP 0x70000000 +#define RISC_SYNC 0x80000000 +#define RISC_RESYNC 0x80008000 +#define RISC_READ 0x90000000 +#define RISC_WRITERM 0xB0000000 +#define RISC_WRITECM 0xC0000000 +#define RISC_WRITECR 0xD0000000 +#define RISC_WRITEC 0x50000000 +#define RISC_READC 0xA0000000 + +#define RISC_SYNC_ODD 0x00000000 +#define RISC_SYNC_EVEN 0x00000200 +#define RISC_SYNC_ODD_VBI 0x00000006 +#define RISC_SYNC_EVEN_VBI 0x00000207 +#define RISC_NOOP 0xF0000000 + +//***************************************************************************** +// ASB SRAM +//***************************************************************************** +#define TX_SRAM 0x000000 // Transmit SRAM + +//***************************************************************************** +#define RX_RAM 0x010000 // Receive SRAM + +//***************************************************************************** +// Application Layer (AL) +//***************************************************************************** +#define DEV_CNTRL2 0x040000 // Device control +#define FLD_RUN_RISC 0x00000020 + +//***************************************************************************** +#define PCI_INT_MSK 0x040010 // PCI interrupt mask +#define PCI_INT_STAT 0x040014 // PCI interrupt status +#define PCI_INT_MSTAT 0x040018 // PCI interrupt masked status +#define FLD_HAMMERHEAD_INT (1 << 27) +#define FLD_UART_INT (1 << 26) +#define FLD_IRQN_INT (1 << 25) +#define FLD_TM_INT (1 << 28) +#define FLD_I2C_3_RACK (1 << 27) +#define FLD_I2C_3_INT (1 << 26) +#define FLD_I2C_2_RACK (1 << 25) +#define FLD_I2C_2_INT (1 << 24) +#define FLD_I2C_1_RACK (1 << 23) +#define FLD_I2C_1_INT (1 << 22) + +#define FLD_APB_DMA_BERR_INT (1 << 21) +#define FLD_AL_WR_BERR_INT (1 << 20) +#define FLD_AL_RD_BERR_INT (1 << 19) +#define FLD_RISC_WR_BERR_INT (1 << 18) +#define FLD_RISC_RD_BERR_INT (1 << 17) + +#define FLD_VID_I_INT (1 << 8) +#define FLD_VID_H_INT (1 << 7) +#define FLD_VID_G_INT (1 << 6) +#define FLD_VID_F_INT (1 << 5) +#define FLD_VID_E_INT (1 << 4) +#define FLD_VID_D_INT (1 << 3) +#define FLD_VID_C_INT (1 << 2) +#define FLD_VID_B_INT (1 << 1) +#define FLD_VID_A_INT (1 << 0) + +//***************************************************************************** +#define VID_A_INT_MSK 0x040020 // Video A interrupt mask +#define VID_A_INT_STAT 0x040024 // Video A interrupt status +#define VID_A_INT_MSTAT 0x040028 // Video A interrupt masked status +#define VID_A_INT_SSTAT 0x04002C // Video A interrupt set status + +//***************************************************************************** +#define VID_B_INT_MSK 0x040030 // Video B interrupt mask +#define VID_B_INT_STAT 0x040034 // Video B interrupt status +#define VID_B_INT_MSTAT 0x040038 // Video B interrupt masked status +#define VID_B_INT_SSTAT 0x04003C // Video B interrupt set status + +//***************************************************************************** +#define VID_C_INT_MSK 0x040040 // Video C interrupt mask +#define VID_C_INT_STAT 0x040044 // Video C interrupt status +#define VID_C_INT_MSTAT 0x040048 // Video C interrupt masked status +#define VID_C_INT_SSTAT 0x04004C // Video C interrupt set status + +//***************************************************************************** +#define VID_D_INT_MSK 0x040050 // Video D interrupt mask +#define VID_D_INT_STAT 0x040054 // Video D interrupt status +#define VID_D_INT_MSTAT 0x040058 // Video D interrupt masked status +#define VID_D_INT_SSTAT 0x04005C // Video D interrupt set status + +//***************************************************************************** +#define VID_E_INT_MSK 0x040060 // Video E interrupt mask +#define VID_E_INT_STAT 0x040064 // Video E interrupt status +#define VID_E_INT_MSTAT 0x040068 // Video E interrupt masked status +#define VID_E_INT_SSTAT 0x04006C // Video E interrupt set status + +//***************************************************************************** +#define VID_F_INT_MSK 0x040070 // Video F interrupt mask +#define VID_F_INT_STAT 0x040074 // Video F interrupt status +#define VID_F_INT_MSTAT 0x040078 // Video F interrupt masked status +#define VID_F_INT_SSTAT 0x04007C // Video F interrupt set status + +//***************************************************************************** +#define VID_G_INT_MSK 0x040080 // Video G interrupt mask +#define VID_G_INT_STAT 0x040084 // Video G interrupt status +#define VID_G_INT_MSTAT 0x040088 // Video G interrupt masked status +#define VID_G_INT_SSTAT 0x04008C // Video G interrupt set status + +//***************************************************************************** +#define VID_H_INT_MSK 0x040090 // Video H interrupt mask +#define VID_H_INT_STAT 0x040094 // Video H interrupt status +#define VID_H_INT_MSTAT 0x040098 // Video H interrupt masked status +#define VID_H_INT_SSTAT 0x04009C // Video H interrupt set status + +//***************************************************************************** +#define VID_I_INT_MSK 0x0400A0 // Video I interrupt mask +#define VID_I_INT_STAT 0x0400A4 // Video I interrupt status +#define VID_I_INT_MSTAT 0x0400A8 // Video I interrupt masked status +#define VID_I_INT_SSTAT 0x0400AC // Video I interrupt set status + +//***************************************************************************** +#define VID_J_INT_MSK 0x0400B0 // Video J interrupt mask +#define VID_J_INT_STAT 0x0400B4 // Video J interrupt status +#define VID_J_INT_MSTAT 0x0400B8 // Video J interrupt masked status +#define VID_J_INT_SSTAT 0x0400BC // Video J interrupt set status + +#define FLD_VID_SRC_OPC_ERR 0x00020000 +#define FLD_VID_DST_OPC_ERR 0x00010000 +#define FLD_VID_SRC_SYNC 0x00002000 +#define FLD_VID_DST_SYNC 0x00001000 +#define FLD_VID_SRC_UF 0x00000200 +#define FLD_VID_DST_OF 0x00000100 +#define FLD_VID_SRC_RISC2 0x00000020 +#define FLD_VID_DST_RISC2 0x00000010 +#define FLD_VID_SRC_RISC1 0x00000002 +#define FLD_VID_DST_RISC1 0x00000001 +#define FLD_VID_SRC_ERRORS FLD_VID_SRC_OPC_ERR | FLD_VID_SRC_SYNC | FLD_VID_SRC_UF +#define FLD_VID_DST_ERRORS FLD_VID_DST_OPC_ERR | FLD_VID_DST_SYNC | FLD_VID_DST_OF + +//***************************************************************************** +#define AUD_A_INT_MSK 0x0400C0 // Audio Int interrupt mask +#define AUD_A_INT_STAT 0x0400C4 // Audio Int interrupt status +#define AUD_A_INT_MSTAT 0x0400C8 // Audio Int interrupt masked status +#define AUD_A_INT_SSTAT 0x0400CC // Audio Int interrupt set status + +//***************************************************************************** +#define AUD_B_INT_MSK 0x0400D0 // Audio Int interrupt mask +#define AUD_B_INT_STAT 0x0400D4 // Audio Int interrupt status +#define AUD_B_INT_MSTAT 0x0400D8 // Audio Int interrupt masked status +#define AUD_B_INT_SSTAT 0x0400DC // Audio Int interrupt set status + +//***************************************************************************** +#define AUD_C_INT_MSK 0x0400E0 // Audio Int interrupt mask +#define AUD_C_INT_STAT 0x0400E4 // Audio Int interrupt status +#define AUD_C_INT_MSTAT 0x0400E8 // Audio Int interrupt masked status +#define AUD_C_INT_SSTAT 0x0400EC // Audio Int interrupt set status + +//***************************************************************************** +#define AUD_D_INT_MSK 0x0400F0 // Audio Int interrupt mask +#define AUD_D_INT_STAT 0x0400F4 // Audio Int interrupt status +#define AUD_D_INT_MSTAT 0x0400F8 // Audio Int interrupt masked status +#define AUD_D_INT_SSTAT 0x0400FC // Audio Int interrupt set status + +//***************************************************************************** +#define AUD_E_INT_MSK 0x040100 // Audio Int interrupt mask +#define AUD_E_INT_STAT 0x040104 // Audio Int interrupt status +#define AUD_E_INT_MSTAT 0x040108 // Audio Int interrupt masked status +#define AUD_E_INT_SSTAT 0x04010C // Audio Int interrupt set status + +#define FLD_AUD_SRC_OPC_ERR 0x00020000 +#define FLD_AUD_DST_OPC_ERR 0x00010000 +#define FLD_AUD_SRC_SYNC 0x00002000 +#define FLD_AUD_DST_SYNC 0x00001000 +#define FLD_AUD_SRC_OF 0x00000200 +#define FLD_AUD_DST_OF 0x00000100 +#define FLD_AUD_SRC_RISCI2 0x00000020 +#define FLD_AUD_DST_RISCI2 0x00000010 +#define FLD_AUD_SRC_RISCI1 0x00000002 +#define FLD_AUD_DST_RISCI1 0x00000001 + +//***************************************************************************** +#define MBIF_A_INT_MSK 0x040110 // MBIF Int interrupt mask +#define MBIF_A_INT_STAT 0x040114 // MBIF Int interrupt status +#define MBIF_A_INT_MSTAT 0x040118 // MBIF Int interrupt masked status +#define MBIF_A_INT_SSTAT 0x04011C // MBIF Int interrupt set status + +//***************************************************************************** +#define MBIF_B_INT_MSK 0x040120 // MBIF Int interrupt mask +#define MBIF_B_INT_STAT 0x040124 // MBIF Int interrupt status +#define MBIF_B_INT_MSTAT 0x040128 // MBIF Int interrupt masked status +#define MBIF_B_INT_SSTAT 0x04012C // MBIF Int interrupt set status + +#define FLD_MBIF_DST_OPC_ERR 0x00010000 +#define FLD_MBIF_DST_SYNC 0x00001000 +#define FLD_MBIF_DST_OF 0x00000100 +#define FLD_MBIF_DST_RISCI2 0x00000010 +#define FLD_MBIF_DST_RISCI1 0x00000001 + +//***************************************************************************** +#define AUD_EXT_INT_MSK 0x040060 // Audio Ext interrupt mask +#define AUD_EXT_INT_STAT 0x040064 // Audio Ext interrupt status +#define AUD_EXT_INT_MSTAT 0x040068 // Audio Ext interrupt masked status +#define AUD_EXT_INT_SSTAT 0x04006C // Audio Ext interrupt set status +#define FLD_AUD_EXT_OPC_ERR 0x00010000 +#define FLD_AUD_EXT_SYNC 0x00001000 +#define FLD_AUD_EXT_OF 0x00000100 +#define FLD_AUD_EXT_RISCI2 0x00000010 +#define FLD_AUD_EXT_RISCI1 0x00000001 + +//***************************************************************************** +#define GPIO_LO 0x110010 // Lower of GPIO pins [31:0] +#define GPIO_HI 0x110014 // Upper WORD of GPIO pins [47:31] + +#define GPIO_LO_OE 0x110018 // Lower of GPIO output enable [31:0] +#define GPIO_HI_OE 0x11001C // Upper word of GPIO output enable [47:32] + +#define GPIO_LO_INT_MSK 0x11003C // GPIO interrupt mask +#define GPIO_LO_INT_STAT 0x110044 // GPIO interrupt status +#define GPIO_LO_INT_MSTAT 0x11004C // GPIO interrupt masked status +#define GPIO_LO_ISM_SNS 0x110054 // GPIO interrupt sensitivity +#define GPIO_LO_ISM_POL 0x11005C // GPIO interrupt polarity + +#define GPIO_HI_INT_MSK 0x110040 // GPIO interrupt mask +#define GPIO_HI_INT_STAT 0x110048 // GPIO interrupt status +#define GPIO_HI_INT_MSTAT 0x110050 // GPIO interrupt masked status +#define GPIO_HI_ISM_SNS 0x110058 // GPIO interrupt sensitivity +#define GPIO_HI_ISM_POL 0x110060 // GPIO interrupt polarity + +#define FLD_GPIO43_INT (1 << 11) +#define FLD_GPIO42_INT (1 << 10) +#define FLD_GPIO41_INT (1 << 9) +#define FLD_GPIO40_INT (1 << 8) + +#define FLD_GPIO9_INT (1 << 9) +#define FLD_GPIO8_INT (1 << 8) +#define FLD_GPIO7_INT (1 << 7) +#define FLD_GPIO6_INT (1 << 6) +#define FLD_GPIO5_INT (1 << 5) +#define FLD_GPIO4_INT (1 << 4) +#define FLD_GPIO3_INT (1 << 3) +#define FLD_GPIO2_INT (1 << 2) +#define FLD_GPIO1_INT (1 << 1) +#define FLD_GPIO0_INT (1 << 0) + +//***************************************************************************** +#define TC_REQ 0x040090 // Rider PCI Express traFFic class request + +//***************************************************************************** +#define TC_REQ_SET 0x040094 // Rider PCI Express traFFic class request set + +//***************************************************************************** +// Rider +//***************************************************************************** + +// PCI Compatible Header +//***************************************************************************** +#define RDR_CFG0 0x050000 +#define RDR_VENDOR_DEVICE_ID_CFG 0x050000 + +//***************************************************************************** +#define RDR_CFG1 0x050004 + +//***************************************************************************** +#define RDR_CFG2 0x050008 + +//***************************************************************************** +#define RDR_CFG3 0x05000C + +//***************************************************************************** +#define RDR_CFG4 0x050010 + +//***************************************************************************** +#define RDR_CFG5 0x050014 + +//***************************************************************************** +#define RDR_CFG6 0x050018 + +//***************************************************************************** +#define RDR_CFG7 0x05001C + +//***************************************************************************** +#define RDR_CFG8 0x050020 + +//***************************************************************************** +#define RDR_CFG9 0x050024 + +//***************************************************************************** +#define RDR_CFGA 0x050028 + +//***************************************************************************** +#define RDR_CFGB 0x05002C +#define RDR_SUSSYSTEM_ID_CFG 0x05002C + +//***************************************************************************** +#define RDR_CFGC 0x050030 + +//***************************************************************************** +#define RDR_CFGD 0x050034 + +//***************************************************************************** +#define RDR_CFGE 0x050038 + +//***************************************************************************** +#define RDR_CFGF 0x05003C + +//***************************************************************************** +// PCI-Express Capabilities +//***************************************************************************** +#define RDR_PECAP 0x050040 + +//***************************************************************************** +#define RDR_PEDEVCAP 0x050044 + +//***************************************************************************** +#define RDR_PEDEVSC 0x050048 + +//***************************************************************************** +#define RDR_PELINKCAP 0x05004C + +//***************************************************************************** +#define RDR_PELINKSC 0x050050 + +//***************************************************************************** +#define RDR_PMICAP 0x050080 + +//***************************************************************************** +#define RDR_PMCSR 0x050084 + +//***************************************************************************** +#define RDR_VPDCAP 0x050090 + +//***************************************************************************** +#define RDR_VPDDATA 0x050094 + +//***************************************************************************** +#define RDR_MSICAP 0x0500A0 + +//***************************************************************************** +#define RDR_MSIARL 0x0500A4 + +//***************************************************************************** +#define RDR_MSIARU 0x0500A8 + +//***************************************************************************** +#define RDR_MSIDATA 0x0500AC + +//***************************************************************************** +// PCI Express Extended Capabilities +//***************************************************************************** +#define RDR_AERXCAP 0x050100 + +//***************************************************************************** +#define RDR_AERUESTA 0x050104 + +//***************************************************************************** +#define RDR_AERUEMSK 0x050108 + +//***************************************************************************** +#define RDR_AERUESEV 0x05010C + +//***************************************************************************** +#define RDR_AERCESTA 0x050110 + +//***************************************************************************** +#define RDR_AERCEMSK 0x050114 + +//***************************************************************************** +#define RDR_AERCC 0x050118 + +//***************************************************************************** +#define RDR_AERHL0 0x05011C + +//***************************************************************************** +#define RDR_AERHL1 0x050120 + +//***************************************************************************** +#define RDR_AERHL2 0x050124 + +//***************************************************************************** +#define RDR_AERHL3 0x050128 + +//***************************************************************************** +#define RDR_VCXCAP 0x050200 + +//***************************************************************************** +#define RDR_VCCAP1 0x050204 + +//***************************************************************************** +#define RDR_VCCAP2 0x050208 + +//***************************************************************************** +#define RDR_VCSC 0x05020C + +//***************************************************************************** +#define RDR_VCR0_CAP 0x050210 + +//***************************************************************************** +#define RDR_VCR0_CTRL 0x050214 + +//***************************************************************************** +#define RDR_VCR0_STAT 0x050218 + +//***************************************************************************** +#define RDR_VCR1_CAP 0x05021C + +//***************************************************************************** +#define RDR_VCR1_CTRL 0x050220 + +//***************************************************************************** +#define RDR_VCR1_STAT 0x050224 + +//***************************************************************************** +#define RDR_VCR2_CAP 0x050228 + +//***************************************************************************** +#define RDR_VCR2_CTRL 0x05022C + +//***************************************************************************** +#define RDR_VCR2_STAT 0x050230 + +//***************************************************************************** +#define RDR_VCR3_CAP 0x050234 + +//***************************************************************************** +#define RDR_VCR3_CTRL 0x050238 + +//***************************************************************************** +#define RDR_VCR3_STAT 0x05023C + +//***************************************************************************** +#define RDR_VCARB0 0x050240 + +//***************************************************************************** +#define RDR_VCARB1 0x050244 + +//***************************************************************************** +#define RDR_VCARB2 0x050248 + +//***************************************************************************** +#define RDR_VCARB3 0x05024C + +//***************************************************************************** +#define RDR_VCARB4 0x050250 + +//***************************************************************************** +#define RDR_VCARB5 0x050254 + +//***************************************************************************** +#define RDR_VCARB6 0x050258 + +//***************************************************************************** +#define RDR_VCARB7 0x05025C + +//***************************************************************************** +#define RDR_RDRSTAT0 0x050300 + +//***************************************************************************** +#define RDR_RDRSTAT1 0x050304 + +//***************************************************************************** +#define RDR_RDRCTL0 0x050308 + +//***************************************************************************** +#define RDR_RDRCTL1 0x05030C + +//***************************************************************************** +// Transaction Layer Registers +//***************************************************************************** +#define RDR_TLSTAT0 0x050310 + +//***************************************************************************** +#define RDR_TLSTAT1 0x050314 + +//***************************************************************************** +#define RDR_TLCTL0 0x050318 +#define FLD_CFG_UR_CPL_MODE 0x00000040 +#define FLD_CFG_CORR_ERR_QUITE 0x00000020 +#define FLD_CFG_RCB_CK_EN 0x00000010 +#define FLD_CFG_BNDRY_CK_EN 0x00000008 +#define FLD_CFG_BYTE_EN_CK_EN 0x00000004 +#define FLD_CFG_RELAX_ORDER_MSK 0x00000002 +#define FLD_CFG_TAG_ORDER_EN 0x00000001 + +//***************************************************************************** +#define RDR_TLCTL1 0x05031C + +//***************************************************************************** +#define RDR_REQRCAL 0x050320 + +//***************************************************************************** +#define RDR_REQRCAU 0x050324 + +//***************************************************************************** +#define RDR_REQEPA 0x050328 + +//***************************************************************************** +#define RDR_REQCTRL 0x05032C + +//***************************************************************************** +#define RDR_REQSTAT 0x050330 + +//***************************************************************************** +#define RDR_TL_TEST 0x050334 + +//***************************************************************************** +#define RDR_VCR01_CTL 0x050348 + +//***************************************************************************** +#define RDR_VCR23_CTL 0x05034C + +//***************************************************************************** +#define RDR_RX_VCR0_FC 0x050350 + +//***************************************************************************** +#define RDR_RX_VCR1_FC 0x050354 + +//***************************************************************************** +#define RDR_RX_VCR2_FC 0x050358 + +//***************************************************************************** +#define RDR_RX_VCR3_FC 0x05035C + +//***************************************************************************** +// Data Link Layer Registers +//***************************************************************************** +#define RDR_DLLSTAT 0x050360 + +//***************************************************************************** +#define RDR_DLLCTRL 0x050364 + +//***************************************************************************** +#define RDR_REPLAYTO 0x050368 + +//***************************************************************************** +#define RDR_ACKLATTO 0x05036C + +//***************************************************************************** +// MAC Layer Registers +//***************************************************************************** +#define RDR_MACSTAT0 0x050380 + +//***************************************************************************** +#define RDR_MACSTAT1 0x050384 + +//***************************************************************************** +#define RDR_MACCTRL0 0x050388 + +//***************************************************************************** +#define RDR_MACCTRL1 0x05038C + +//***************************************************************************** +#define RDR_MACCTRL2 0x050390 + +//***************************************************************************** +#define RDR_MAC_LB_DATA 0x050394 + +//***************************************************************************** +#define RDR_L0S_EXIT_LAT 0x050398 + +//***************************************************************************** +// DMAC +//***************************************************************************** +#define DMA1_PTR1 0x100000 // DMA Current Ptr : Ch#1 + +//***************************************************************************** +#define DMA2_PTR1 0x100004 // DMA Current Ptr : Ch#2 + +//***************************************************************************** +#define DMA3_PTR1 0x100008 // DMA Current Ptr : Ch#3 + +//***************************************************************************** +#define DMA4_PTR1 0x10000C // DMA Current Ptr : Ch#4 + +//***************************************************************************** +#define DMA5_PTR1 0x100010 // DMA Current Ptr : Ch#5 + +//***************************************************************************** +#define DMA6_PTR1 0x100014 // DMA Current Ptr : Ch#6 + +//***************************************************************************** +#define DMA7_PTR1 0x100018 // DMA Current Ptr : Ch#7 + +//***************************************************************************** +#define DMA8_PTR1 0x10001C // DMA Current Ptr : Ch#8 + +//***************************************************************************** +#define DMA9_PTR1 0x100020 // DMA Current Ptr : Ch#9 + +//***************************************************************************** +#define DMA10_PTR1 0x100024 // DMA Current Ptr : Ch#10 + +//***************************************************************************** +#define DMA11_PTR1 0x100028 // DMA Current Ptr : Ch#11 + +//***************************************************************************** +#define DMA12_PTR1 0x10002C // DMA Current Ptr : Ch#12 + +//***************************************************************************** +#define DMA13_PTR1 0x100030 // DMA Current Ptr : Ch#13 + +//***************************************************************************** +#define DMA14_PTR1 0x100034 // DMA Current Ptr : Ch#14 + +//***************************************************************************** +#define DMA15_PTR1 0x100038 // DMA Current Ptr : Ch#15 + +//***************************************************************************** +#define DMA16_PTR1 0x10003C // DMA Current Ptr : Ch#16 + +//***************************************************************************** +#define DMA17_PTR1 0x100040 // DMA Current Ptr : Ch#17 + +//***************************************************************************** +#define DMA18_PTR1 0x100044 // DMA Current Ptr : Ch#18 + +//***************************************************************************** +#define DMA19_PTR1 0x100048 // DMA Current Ptr : Ch#19 + +//***************************************************************************** +#define DMA20_PTR1 0x10004C // DMA Current Ptr : Ch#20 + +//***************************************************************************** +#define DMA21_PTR1 0x100050 // DMA Current Ptr : Ch#21 + +//***************************************************************************** +#define DMA22_PTR1 0x100054 // DMA Current Ptr : Ch#22 + +//***************************************************************************** +#define DMA23_PTR1 0x100058 // DMA Current Ptr : Ch#23 + +//***************************************************************************** +#define DMA24_PTR1 0x10005C // DMA Current Ptr : Ch#24 + +//***************************************************************************** +#define DMA25_PTR1 0x100060 // DMA Current Ptr : Ch#25 + +//***************************************************************************** +#define DMA26_PTR1 0x100064 // DMA Current Ptr : Ch#26 + +//***************************************************************************** +#define DMA1_PTR2 0x100080 // DMA Tab Ptr : Ch#1 + +//***************************************************************************** +#define DMA2_PTR2 0x100084 // DMA Tab Ptr : Ch#2 + +//***************************************************************************** +#define DMA3_PTR2 0x100088 // DMA Tab Ptr : Ch#3 + +//***************************************************************************** +#define DMA4_PTR2 0x10008C // DMA Tab Ptr : Ch#4 + +//***************************************************************************** +#define DMA5_PTR2 0x100090 // DMA Tab Ptr : Ch#5 + +//***************************************************************************** +#define DMA6_PTR2 0x100094 // DMA Tab Ptr : Ch#6 + +//***************************************************************************** +#define DMA7_PTR2 0x100098 // DMA Tab Ptr : Ch#7 + +//***************************************************************************** +#define DMA8_PTR2 0x10009C // DMA Tab Ptr : Ch#8 + +//***************************************************************************** +#define DMA9_PTR2 0x1000A0 // DMA Tab Ptr : Ch#9 + +//***************************************************************************** +#define DMA10_PTR2 0x1000A4 // DMA Tab Ptr : Ch#10 + +//***************************************************************************** +#define DMA11_PTR2 0x1000A8 // DMA Tab Ptr : Ch#11 + +//***************************************************************************** +#define DMA12_PTR2 0x1000AC // DMA Tab Ptr : Ch#12 + +//***************************************************************************** +#define DMA13_PTR2 0x1000B0 // DMA Tab Ptr : Ch#13 + +//***************************************************************************** +#define DMA14_PTR2 0x1000B4 // DMA Tab Ptr : Ch#14 + +//***************************************************************************** +#define DMA15_PTR2 0x1000B8 // DMA Tab Ptr : Ch#15 + +//***************************************************************************** +#define DMA16_PTR2 0x1000BC // DMA Tab Ptr : Ch#16 + +//***************************************************************************** +#define DMA17_PTR2 0x1000C0 // DMA Tab Ptr : Ch#17 + +//***************************************************************************** +#define DMA18_PTR2 0x1000C4 // DMA Tab Ptr : Ch#18 + +//***************************************************************************** +#define DMA19_PTR2 0x1000C8 // DMA Tab Ptr : Ch#19 + +//***************************************************************************** +#define DMA20_PTR2 0x1000CC // DMA Tab Ptr : Ch#20 + +//***************************************************************************** +#define DMA21_PTR2 0x1000D0 // DMA Tab Ptr : Ch#21 + +//***************************************************************************** +#define DMA22_PTR2 0x1000D4 // DMA Tab Ptr : Ch#22 + +//***************************************************************************** +#define DMA23_PTR2 0x1000D8 // DMA Tab Ptr : Ch#23 + +//***************************************************************************** +#define DMA24_PTR2 0x1000DC // DMA Tab Ptr : Ch#24 + +//***************************************************************************** +#define DMA25_PTR2 0x1000E0 // DMA Tab Ptr : Ch#25 + +//***************************************************************************** +#define DMA26_PTR2 0x1000E4 // DMA Tab Ptr : Ch#26 + +//***************************************************************************** +#define DMA1_CNT1 0x100100 // DMA BuFFer Size : Ch#1 + +//***************************************************************************** +#define DMA2_CNT1 0x100104 // DMA BuFFer Size : Ch#2 + +//***************************************************************************** +#define DMA3_CNT1 0x100108 // DMA BuFFer Size : Ch#3 + +//***************************************************************************** +#define DMA4_CNT1 0x10010C // DMA BuFFer Size : Ch#4 + +//***************************************************************************** +#define DMA5_CNT1 0x100110 // DMA BuFFer Size : Ch#5 + +//***************************************************************************** +#define DMA6_CNT1 0x100114 // DMA BuFFer Size : Ch#6 + +//***************************************************************************** +#define DMA7_CNT1 0x100118 // DMA BuFFer Size : Ch#7 + +//***************************************************************************** +#define DMA8_CNT1 0x10011C // DMA BuFFer Size : Ch#8 + +//***************************************************************************** +#define DMA9_CNT1 0x100120 // DMA BuFFer Size : Ch#9 + +//***************************************************************************** +#define DMA10_CNT1 0x100124 // DMA BuFFer Size : Ch#10 + +//***************************************************************************** +#define DMA11_CNT1 0x100128 // DMA BuFFer Size : Ch#11 + +//***************************************************************************** +#define DMA12_CNT1 0x10012C // DMA BuFFer Size : Ch#12 + +//***************************************************************************** +#define DMA13_CNT1 0x100130 // DMA BuFFer Size : Ch#13 + +//***************************************************************************** +#define DMA14_CNT1 0x100134 // DMA BuFFer Size : Ch#14 + +//***************************************************************************** +#define DMA15_CNT1 0x100138 // DMA BuFFer Size : Ch#15 + +//***************************************************************************** +#define DMA16_CNT1 0x10013C // DMA BuFFer Size : Ch#16 + +//***************************************************************************** +#define DMA17_CNT1 0x100140 // DMA BuFFer Size : Ch#17 + +//***************************************************************************** +#define DMA18_CNT1 0x100144 // DMA BuFFer Size : Ch#18 + +//***************************************************************************** +#define DMA19_CNT1 0x100148 // DMA BuFFer Size : Ch#19 + +//***************************************************************************** +#define DMA20_CNT1 0x10014C // DMA BuFFer Size : Ch#20 + +//***************************************************************************** +#define DMA21_CNT1 0x100150 // DMA BuFFer Size : Ch#21 + +//***************************************************************************** +#define DMA22_CNT1 0x100154 // DMA BuFFer Size : Ch#22 + +//***************************************************************************** +#define DMA23_CNT1 0x100158 // DMA BuFFer Size : Ch#23 + +//***************************************************************************** +#define DMA24_CNT1 0x10015C // DMA BuFFer Size : Ch#24 + +//***************************************************************************** +#define DMA25_CNT1 0x100160 // DMA BuFFer Size : Ch#25 + +//***************************************************************************** +#define DMA26_CNT1 0x100164 // DMA BuFFer Size : Ch#26 + +//***************************************************************************** +#define DMA1_CNT2 0x100180 // DMA Table Size : Ch#1 + +//***************************************************************************** +#define DMA2_CNT2 0x100184 // DMA Table Size : Ch#2 + +//***************************************************************************** +#define DMA3_CNT2 0x100188 // DMA Table Size : Ch#3 + +//***************************************************************************** +#define DMA4_CNT2 0x10018C // DMA Table Size : Ch#4 + +//***************************************************************************** +#define DMA5_CNT2 0x100190 // DMA Table Size : Ch#5 + +//***************************************************************************** +#define DMA6_CNT2 0x100194 // DMA Table Size : Ch#6 + +//***************************************************************************** +#define DMA7_CNT2 0x100198 // DMA Table Size : Ch#7 + +//***************************************************************************** +#define DMA8_CNT2 0x10019C // DMA Table Size : Ch#8 + +//***************************************************************************** +#define DMA9_CNT2 0x1001A0 // DMA Table Size : Ch#9 + +//***************************************************************************** +#define DMA10_CNT2 0x1001A4 // DMA Table Size : Ch#10 + +//***************************************************************************** +#define DMA11_CNT2 0x1001A8 // DMA Table Size : Ch#11 + +//***************************************************************************** +#define DMA12_CNT2 0x1001AC // DMA Table Size : Ch#12 + +//***************************************************************************** +#define DMA13_CNT2 0x1001B0 // DMA Table Size : Ch#13 + +//***************************************************************************** +#define DMA14_CNT2 0x1001B4 // DMA Table Size : Ch#14 + +//***************************************************************************** +#define DMA15_CNT2 0x1001B8 // DMA Table Size : Ch#15 + +//***************************************************************************** +#define DMA16_CNT2 0x1001BC // DMA Table Size : Ch#16 + +//***************************************************************************** +#define DMA17_CNT2 0x1001C0 // DMA Table Size : Ch#17 + +//***************************************************************************** +#define DMA18_CNT2 0x1001C4 // DMA Table Size : Ch#18 + +//***************************************************************************** +#define DMA19_CNT2 0x1001C8 // DMA Table Size : Ch#19 + +//***************************************************************************** +#define DMA20_CNT2 0x1001CC // DMA Table Size : Ch#20 + +//***************************************************************************** +#define DMA21_CNT2 0x1001D0 // DMA Table Size : Ch#21 + +//***************************************************************************** +#define DMA22_CNT2 0x1001D4 // DMA Table Size : Ch#22 + +//***************************************************************************** +#define DMA23_CNT2 0x1001D8 // DMA Table Size : Ch#23 + +//***************************************************************************** +#define DMA24_CNT2 0x1001DC // DMA Table Size : Ch#24 + +//***************************************************************************** +#define DMA25_CNT2 0x1001E0 // DMA Table Size : Ch#25 + +//***************************************************************************** +#define DMA26_CNT2 0x1001E4 // DMA Table Size : Ch#26 + +//***************************************************************************** + // ITG +//***************************************************************************** +#define TM_CNT_LDW 0x110000 // Timer : Counter low + +//***************************************************************************** +#define TM_CNT_UW 0x110004 // Timer : Counter high word + +//***************************************************************************** +#define TM_LMT_LDW 0x110008 // Timer : Limit low + +//***************************************************************************** +#define TM_LMT_UW 0x11000C // Timer : Limit high word + +//***************************************************************************** +#define GP0_IO 0x110010 // GPIO output enables data I/O +#define FLD_GP_OE 0x00FF0000 // GPIO: GP_OE output enable +#define FLD_GP_IN 0x0000FF00 // GPIO: GP_IN status +#define FLD_GP_OUT 0x000000FF // GPIO: GP_OUT control + +//***************************************************************************** +#define GPIO_ISM 0x110014 // GPIO interrupt sensitivity mode +#define FLD_GP_ISM_SNS 0x00000070 +#define FLD_GP_ISM_POL 0x00000007 + +//***************************************************************************** +#define SOFT_RESET 0x11001C // Output system reset reg +#define FLD_PECOS_SOFT_RESET 0x00000001 + +//***************************************************************************** +#define MC416_RWD 0x110020 // MC416 GPIO[18:3] pin +#define MC416_OEN 0x110024 // Output enable of GPIO[18:3] +#define MC416_CTL 0x110028 + +//***************************************************************************** +#define ALT_PIN_OUT_SEL 0x11002C // Alternate GPIO output select + +#define FLD_ALT_GPIO_OUT_SEL 0xF0000000 +// 0 Disabled <-- default +// 1 GPIO[0] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] +// 8 ATT_IF + +#define FLD_AUX_PLL_CLK_ALT_SEL 0x0F000000 +// 0 AUX_PLL_CLK<-- default +// 1 GPIO[2] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] + +#define FLD_IR_TX_ALT_SEL 0x00F00000 +// 0 IR_TX <-- default +// 1 GPIO[1] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] + +#define FLD_IR_RX_ALT_SEL 0x000F0000 +// 0 IR_RX <-- default +// 1 GPIO[0] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] + +#define FLD_GPIO10_ALT_SEL 0x0000F000 +// 0 GPIO[10] <-- default +// 1 GPIO[0] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] + +#define FLD_GPIO2_ALT_SEL 0x00000F00 +// 0 GPIO[2] <-- default +// 1 GPIO[1] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] + +#define FLD_GPIO1_ALT_SEL 0x000000F0 +// 0 GPIO[1] <-- default +// 1 GPIO[0] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] + +#define FLD_GPIO0_ALT_SEL 0x0000000F +// 0 GPIO[0] <-- default +// 1 GPIO[1] +// 2 GPIO[10] +// 3 VIP_656_DATA_VAL +// 4 VIP_656_DATA[0] +// 5 VIP_656_CLK +// 6 VIP_656_DATA_EXT[1] +// 7 VIP_656_DATA_EXT[0] + +#define ALT_PIN_IN_SEL 0x110030 // Alternate GPIO input select + +#define FLD_GPIO10_ALT_IN_SEL 0x0000F000 +// 0 GPIO[10] <-- default +// 1 IR_RX +// 2 IR_TX +// 3 AUX_PLL_CLK +// 4 IF_ATT_SEL +// 5 GPIO[0] +// 6 GPIO[1] +// 7 GPIO[2] + +#define FLD_GPIO2_ALT_IN_SEL 0x00000F00 +// 0 GPIO[2] <-- default +// 1 IR_RX +// 2 IR_TX +// 3 AUX_PLL_CLK +// 4 IF_ATT_SEL + +#define FLD_GPIO1_ALT_IN_SEL 0x000000F0 +// 0 GPIO[1] <-- default +// 1 IR_RX +// 2 IR_TX +// 3 AUX_PLL_CLK +// 4 IF_ATT_SEL + +#define FLD_GPIO0_ALT_IN_SEL 0x0000000F +// 0 GPIO[0] <-- default +// 1 IR_RX +// 2 IR_TX +// 3 AUX_PLL_CLK +// 4 IF_ATT_SEL + +//***************************************************************************** +#define TEST_BUS_CTL1 0x110040 // Test bus control register #1 + +//***************************************************************************** +#define TEST_BUS_CTL2 0x110044 // Test bus control register #2 + +//***************************************************************************** +#define CLK_DELAY 0x110048 // Clock delay +#define FLD_MOE_CLK_DIS 0x80000000 // Disable MoE clock + +//***************************************************************************** +#define PAD_CTRL 0x110068 // Pad drive strength control + +//***************************************************************************** +#define MBIST_CTRL 0x110050 // SRAM memory built-in self test control + +//***************************************************************************** +#define MBIST_STAT 0x110054 // SRAM memory built-in self test status + +//***************************************************************************** +// PLL registers +//***************************************************************************** +#define PLL_A_INT_FRAC 0x110088 +#define PLL_A_POST_STAT_BIST 0x11008C +#define PLL_B_INT_FRAC 0x110090 +#define PLL_B_POST_STAT_BIST 0x110094 +#define PLL_C_INT_FRAC 0x110098 +#define PLL_C_POST_STAT_BIST 0x11009C +#define PLL_D_INT_FRAC 0x1100A0 +#define PLL_D_POST_STAT_BIST 0x1100A4 + +#define CLK_RST 0x11002C +#define FLD_VID_I_CLK_NOE 0x00001000 +#define FLD_VID_J_CLK_NOE 0x00002000 +#define FLD_USE_ALT_PLL_REF 0x00004000 + +#define VID_CH_MODE_SEL 0x110078 +#define VID_CH_CLK_SEL 0x11007C + +//***************************************************************************** +#define VBI_A_DMA 0x130008 // VBI A DMA data port + +//***************************************************************************** +#define VID_A_VIP_CTL 0x130080 // Video A VIP format control +#define FLD_VIP_MODE 0x00000001 + +//***************************************************************************** +#define VID_A_PIXEL_FRMT 0x130084 // Video A pixel format +#define FLD_VID_A_GAMMA_DIS 0x00000008 +#define FLD_VID_A_FORMAT 0x00000007 +#define FLD_VID_A_GAMMA_FACTOR 0x00000010 + +//***************************************************************************** +#define VID_A_VBI_CTL 0x130088 // Video A VBI miscellaneous control +#define FLD_VID_A_VIP_EXT 0x00000003 + +//***************************************************************************** +#define VID_B_DMA 0x130100 // Video B DMA data port + +//***************************************************************************** +#define VBI_B_DMA 0x130108 // VBI B DMA data port + +//***************************************************************************** +#define VID_B_SRC_SEL 0x130144 // Video B source select +#define FLD_VID_B_SRC_SEL 0x00000000 + +//***************************************************************************** +#define VID_B_LNGTH 0x130150 // Video B line length +#define FLD_VID_B_LN_LNGTH 0x00000FFF + +//***************************************************************************** +#define VID_B_VIP_CTL 0x130180 // Video B VIP format control + +//***************************************************************************** +#define VID_B_PIXEL_FRMT 0x130184 // Video B pixel format +#define FLD_VID_B_GAMMA_DIS 0x00000008 +#define FLD_VID_B_FORMAT 0x00000007 +#define FLD_VID_B_GAMMA_FACTOR 0x00000010 + +//***************************************************************************** +#define VID_C_DMA 0x130200 // Video C DMA data port + +//***************************************************************************** +#define VID_C_LNGTH 0x130250 // Video C line length +#define FLD_VID_C_LN_LNGTH 0x00000FFF + +//***************************************************************************** +// Video Destination Channels +//***************************************************************************** + +#define VID_DST_A_GPCNT 0x130020 // Video A general purpose counter +#define VID_DST_B_GPCNT 0x130120 // Video B general purpose counter +#define VID_DST_C_GPCNT 0x130220 // Video C general purpose counter +#define VID_DST_D_GPCNT 0x130320 // Video D general purpose counter +#define VID_DST_E_GPCNT 0x130420 // Video E general purpose counter +#define VID_DST_F_GPCNT 0x130520 // Video F general purpose counter +#define VID_DST_G_GPCNT 0x130620 // Video G general purpose counter +#define VID_DST_H_GPCNT 0x130720 // Video H general purpose counter + +//***************************************************************************** + +#define VID_DST_A_GPCNT_CTL 0x130030 // Video A general purpose control +#define VID_DST_B_GPCNT_CTL 0x130130 // Video B general purpose control +#define VID_DST_C_GPCNT_CTL 0x130230 // Video C general purpose control +#define VID_DST_D_GPCNT_CTL 0x130330 // Video D general purpose control +#define VID_DST_E_GPCNT_CTL 0x130430 // Video E general purpose control +#define VID_DST_F_GPCNT_CTL 0x130530 // Video F general purpose control +#define VID_DST_G_GPCNT_CTL 0x130630 // Video G general purpose control +#define VID_DST_H_GPCNT_CTL 0x130730 // Video H general purpose control + +//***************************************************************************** + +#define VID_DST_A_DMA_CTL 0x130040 // Video A DMA control +#define VID_DST_B_DMA_CTL 0x130140 // Video B DMA control +#define VID_DST_C_DMA_CTL 0x130240 // Video C DMA control +#define VID_DST_D_DMA_CTL 0x130340 // Video D DMA control +#define VID_DST_E_DMA_CTL 0x130440 // Video E DMA control +#define VID_DST_F_DMA_CTL 0x130540 // Video F DMA control +#define VID_DST_G_DMA_CTL 0x130640 // Video G DMA control +#define VID_DST_H_DMA_CTL 0x130740 // Video H DMA control + +#define FLD_VID_RISC_EN 0x00000010 +#define FLD_VID_FIFO_EN 0x00000001 + +//***************************************************************************** + +#define VID_DST_A_VIP_CTL 0x130080 // Video A VIP control +#define VID_DST_B_VIP_CTL 0x130180 // Video B VIP control +#define VID_DST_C_VIP_CTL 0x130280 // Video C VIP control +#define VID_DST_D_VIP_CTL 0x130380 // Video D VIP control +#define VID_DST_E_VIP_CTL 0x130480 // Video E VIP control +#define VID_DST_F_VIP_CTL 0x130580 // Video F VIP control +#define VID_DST_G_VIP_CTL 0x130680 // Video G VIP control +#define VID_DST_H_VIP_CTL 0x130780 // Video H VIP control + +//***************************************************************************** + +#define VID_DST_A_PIX_FRMT 0x130084 // Video A Pixel format +#define VID_DST_B_PIX_FRMT 0x130184 // Video B Pixel format +#define VID_DST_C_PIX_FRMT 0x130284 // Video C Pixel format +#define VID_DST_D_PIX_FRMT 0x130384 // Video D Pixel format +#define VID_DST_E_PIX_FRMT 0x130484 // Video E Pixel format +#define VID_DST_F_PIX_FRMT 0x130584 // Video F Pixel format +#define VID_DST_G_PIX_FRMT 0x130684 // Video G Pixel format +#define VID_DST_H_PIX_FRMT 0x130784 // Video H Pixel format + +//***************************************************************************** +// Video Source Channels +//***************************************************************************** + +#define VID_SRC_A_GPCNT_CTL 0x130804 // Video A general purpose control +#define VID_SRC_B_GPCNT_CTL 0x130904 // Video B general purpose control +#define VID_SRC_C_GPCNT_CTL 0x130A04 // Video C general purpose control +#define VID_SRC_D_GPCNT_CTL 0x130B04 // Video D general purpose control +#define VID_SRC_E_GPCNT_CTL 0x130C04 // Video E general purpose control +#define VID_SRC_F_GPCNT_CTL 0x130D04 // Video F general purpose control +#define VID_SRC_I_GPCNT_CTL 0x130E04 // Video I general purpose control +#define VID_SRC_J_GPCNT_CTL 0x130F04 // Video J general purpose control + +//***************************************************************************** + +#define VID_SRC_A_GPCNT 0x130808 // Video A general purpose counter +#define VID_SRC_B_GPCNT 0x130908 // Video B general purpose counter +#define VID_SRC_C_GPCNT 0x130A08 // Video C general purpose counter +#define VID_SRC_D_GPCNT 0x130B08 // Video D general purpose counter +#define VID_SRC_E_GPCNT 0x130C08 // Video E general purpose counter +#define VID_SRC_F_GPCNT 0x130D08 // Video F general purpose counter +#define VID_SRC_I_GPCNT 0x130E08 // Video I general purpose counter +#define VID_SRC_J_GPCNT 0x130F08 // Video J general purpose counter + +//***************************************************************************** + +#define VID_SRC_A_DMA_CTL 0x13080C // Video A DMA control +#define VID_SRC_B_DMA_CTL 0x13090C // Video B DMA control +#define VID_SRC_C_DMA_CTL 0x130A0C // Video C DMA control +#define VID_SRC_D_DMA_CTL 0x130B0C // Video D DMA control +#define VID_SRC_E_DMA_CTL 0x130C0C // Video E DMA control +#define VID_SRC_F_DMA_CTL 0x130D0C // Video F DMA control +#define VID_SRC_I_DMA_CTL 0x130E0C // Video I DMA control +#define VID_SRC_J_DMA_CTL 0x130F0C // Video J DMA control + +#define FLD_APB_RISC_EN 0x00000010 +#define FLD_APB_FIFO_EN 0x00000001 + +//***************************************************************************** + +#define VID_SRC_A_FMT_CTL 0x130810 // Video A format control +#define VID_SRC_B_FMT_CTL 0x130910 // Video B format control +#define VID_SRC_C_FMT_CTL 0x130A10 // Video C format control +#define VID_SRC_D_FMT_CTL 0x130B10 // Video D format control +#define VID_SRC_E_FMT_CTL 0x130C10 // Video E format control +#define VID_SRC_F_FMT_CTL 0x130D10 // Video F format control +#define VID_SRC_I_FMT_CTL 0x130E10 // Video I format control +#define VID_SRC_J_FMT_CTL 0x130F10 // Video J format control + +//***************************************************************************** + +#define VID_SRC_A_ACTIVE_CTL1 0x130814 // Video A active control 1 +#define VID_SRC_B_ACTIVE_CTL1 0x130914 // Video B active control 1 +#define VID_SRC_C_ACTIVE_CTL1 0x130A14 // Video C active control 1 +#define VID_SRC_D_ACTIVE_CTL1 0x130B14 // Video D active control 1 +#define VID_SRC_E_ACTIVE_CTL1 0x130C14 // Video E active control 1 +#define VID_SRC_F_ACTIVE_CTL1 0x130D14 // Video F active control 1 +#define VID_SRC_I_ACTIVE_CTL1 0x130E14 // Video I active control 1 +#define VID_SRC_J_ACTIVE_CTL1 0x130F14 // Video J active control 1 + +//***************************************************************************** + +#define VID_SRC_A_ACTIVE_CTL2 0x130818 // Video A active control 2 +#define VID_SRC_B_ACTIVE_CTL2 0x130918 // Video B active control 2 +#define VID_SRC_C_ACTIVE_CTL2 0x130A18 // Video C active control 2 +#define VID_SRC_D_ACTIVE_CTL2 0x130B18 // Video D active control 2 +#define VID_SRC_E_ACTIVE_CTL2 0x130C18 // Video E active control 2 +#define VID_SRC_F_ACTIVE_CTL2 0x130D18 // Video F active control 2 +#define VID_SRC_I_ACTIVE_CTL2 0x130E18 // Video I active control 2 +#define VID_SRC_J_ACTIVE_CTL2 0x130F18 // Video J active control 2 + +//***************************************************************************** + +#define VID_SRC_A_CDT_SZ 0x13081C // Video A CDT size +#define VID_SRC_B_CDT_SZ 0x13091C // Video B CDT size +#define VID_SRC_C_CDT_SZ 0x130A1C // Video C CDT size +#define VID_SRC_D_CDT_SZ 0x130B1C // Video D CDT size +#define VID_SRC_E_CDT_SZ 0x130C1C // Video E CDT size +#define VID_SRC_F_CDT_SZ 0x130D1C // Video F CDT size +#define VID_SRC_I_CDT_SZ 0x130E1C // Video I CDT size +#define VID_SRC_J_CDT_SZ 0x130F1C // Video J CDT size + +//***************************************************************************** +// Audio I/F +//***************************************************************************** +#define AUD_DST_A_DMA 0x140000 // Audio Int A DMA data port +#define AUD_SRC_A_DMA 0x140008 // Audio Int A DMA data port + +#define AUD_A_GPCNT 0x140010 // Audio Int A gp counter +#define FLD_AUD_A_GP_CNT 0x0000FFFF + +#define AUD_A_GPCNT_CTL 0x140014 // Audio Int A gp control + +#define AUD_A_LNGTH 0x140018 // Audio Int A line length + +#define AUD_A_CFG 0x14001C // Audio Int A configuration + +//***************************************************************************** +#define AUD_DST_B_DMA 0x140100 // Audio Int B DMA data port +#define AUD_SRC_B_DMA 0x140108 // Audio Int B DMA data port + +#define AUD_B_GPCNT 0x140110 // Audio Int B gp counter +#define FLD_AUD_B_GP_CNT 0x0000FFFF + +#define AUD_B_GPCNT_CTL 0x140114 // Audio Int B gp control + +#define AUD_B_LNGTH 0x140118 // Audio Int B line length + +#define AUD_B_CFG 0x14011C // Audio Int B configuration + +//***************************************************************************** +#define AUD_DST_C_DMA 0x140200 // Audio Int C DMA data port +#define AUD_SRC_C_DMA 0x140208 // Audio Int C DMA data port + +#define AUD_C_GPCNT 0x140210 // Audio Int C gp counter +#define FLD_AUD_C_GP_CNT 0x0000FFFF + +#define AUD_C_GPCNT_CTL 0x140214 // Audio Int C gp control + +#define AUD_C_LNGTH 0x140218 // Audio Int C line length + +#define AUD_C_CFG 0x14021C // Audio Int C configuration + +//***************************************************************************** +#define AUD_DST_D_DMA 0x140300 // Audio Int D DMA data port +#define AUD_SRC_D_DMA 0x140308 // Audio Int D DMA data port + +#define AUD_D_GPCNT 0x140310 // Audio Int D gp counter +#define FLD_AUD_D_GP_CNT 0x0000FFFF + +#define AUD_D_GPCNT_CTL 0x140314 // Audio Int D gp control + +#define AUD_D_LNGTH 0x140318 // Audio Int D line length + +#define AUD_D_CFG 0x14031C // Audio Int D configuration + +//***************************************************************************** +#define AUD_SRC_E_DMA 0x140400 // Audio Int E DMA data port + +#define AUD_E_GPCNT 0x140410 // Audio Int E gp counter +#define FLD_AUD_E_GP_CNT 0x0000FFFF + +#define AUD_E_GPCNT_CTL 0x140414 // Audio Int E gp control + +#define AUD_E_CFG 0x14041C // Audio Int E configuration + +//***************************************************************************** + +#define FLD_AUD_DST_LN_LNGTH 0x00000FFF + +#define FLD_AUD_DST_PK_MODE 0x00004000 + +#define FLD_AUD_CLK_ENABLE 0x00000200 + +#define FLD_AUD_MASTER_MODE 0x00000002 + +#define FLD_AUD_SONY_MODE 0x00000001 + +#define FLD_AUD_CLK_SELECT_PLL_D 0x00001800 + +#define FLD_AUD_DST_ENABLE 0x00020000 + +#define FLD_AUD_SRC_ENABLE 0x00010000 + +//***************************************************************************** +#define AUD_INT_DMA_CTL 0x140500 // Audio Int DMA control + +#define FLD_AUD_SRC_E_RISC_EN 0x00008000 +#define FLD_AUD_SRC_C_RISC_EN 0x00004000 +#define FLD_AUD_SRC_B_RISC_EN 0x00002000 +#define FLD_AUD_SRC_A_RISC_EN 0x00001000 + +#define FLD_AUD_DST_D_RISC_EN 0x00000800 +#define FLD_AUD_DST_C_RISC_EN 0x00000400 +#define FLD_AUD_DST_B_RISC_EN 0x00000200 +#define FLD_AUD_DST_A_RISC_EN 0x00000100 + +#define FLD_AUD_SRC_E_FIFO_EN 0x00000080 +#define FLD_AUD_SRC_C_FIFO_EN 0x00000040 +#define FLD_AUD_SRC_B_FIFO_EN 0x00000020 +#define FLD_AUD_SRC_A_FIFO_EN 0x00000010 + +#define FLD_AUD_DST_D_FIFO_EN 0x00000008 +#define FLD_AUD_DST_C_FIFO_EN 0x00000004 +#define FLD_AUD_DST_B_FIFO_EN 0x00000002 +#define FLD_AUD_DST_A_FIFO_EN 0x00000001 + +//***************************************************************************** +// +// Mobilygen Interface Registers +// +//***************************************************************************** +// Mobilygen Interface A +//***************************************************************************** +#define MB_IF_A_DMA 0x150000 // MBIF A DMA data port +#define MB_IF_A_GPCN 0x150008 // MBIF A GP counter +#define MB_IF_A_GPCN_CTRL 0x15000C +#define MB_IF_A_DMA_CTRL 0x150010 +#define MB_IF_A_LENGTH 0x150014 +#define MB_IF_A_HDMA_XFER_SZ 0x150018 +#define MB_IF_A_HCMD 0x15001C +#define MB_IF_A_HCONFIG 0x150020 +#define MB_IF_A_DATA_STRUCT_0 0x150024 +#define MB_IF_A_DATA_STRUCT_1 0x150028 +#define MB_IF_A_DATA_STRUCT_2 0x15002C +#define MB_IF_A_DATA_STRUCT_3 0x150030 +#define MB_IF_A_DATA_STRUCT_4 0x150034 +#define MB_IF_A_DATA_STRUCT_5 0x150038 +#define MB_IF_A_DATA_STRUCT_6 0x15003C +#define MB_IF_A_DATA_STRUCT_7 0x150040 +#define MB_IF_A_DATA_STRUCT_8 0x150044 +#define MB_IF_A_DATA_STRUCT_9 0x150048 +#define MB_IF_A_DATA_STRUCT_A 0x15004C +#define MB_IF_A_DATA_STRUCT_B 0x150050 +#define MB_IF_A_DATA_STRUCT_C 0x150054 +#define MB_IF_A_DATA_STRUCT_D 0x150058 +#define MB_IF_A_DATA_STRUCT_E 0x15005C +#define MB_IF_A_DATA_STRUCT_F 0x150060 +//***************************************************************************** +// Mobilygen Interface B +//***************************************************************************** +#define MB_IF_B_DMA 0x160000 // MBIF A DMA data port +#define MB_IF_B_GPCN 0x160008 // MBIF A GP counter +#define MB_IF_B_GPCN_CTRL 0x16000C +#define MB_IF_B_DMA_CTRL 0x160010 +#define MB_IF_B_LENGTH 0x160014 +#define MB_IF_B_HDMA_XFER_SZ 0x160018 +#define MB_IF_B_HCMD 0x16001C +#define MB_IF_B_HCONFIG 0x160020 +#define MB_IF_B_DATA_STRUCT_0 0x160024 +#define MB_IF_B_DATA_STRUCT_1 0x160028 +#define MB_IF_B_DATA_STRUCT_2 0x16002C +#define MB_IF_B_DATA_STRUCT_3 0x160030 +#define MB_IF_B_DATA_STRUCT_4 0x160034 +#define MB_IF_B_DATA_STRUCT_5 0x160038 +#define MB_IF_B_DATA_STRUCT_6 0x16003C +#define MB_IF_B_DATA_STRUCT_7 0x160040 +#define MB_IF_B_DATA_STRUCT_8 0x160044 +#define MB_IF_B_DATA_STRUCT_9 0x160048 +#define MB_IF_B_DATA_STRUCT_A 0x16004C +#define MB_IF_B_DATA_STRUCT_B 0x160050 +#define MB_IF_B_DATA_STRUCT_C 0x160054 +#define MB_IF_B_DATA_STRUCT_D 0x160058 +#define MB_IF_B_DATA_STRUCT_E 0x16005C +#define MB_IF_B_DATA_STRUCT_F 0x160060 + +// MB_DMA_CTRL +#define FLD_MB_IF_RISC_EN 0x00000010 +#define FLD_MB_IF_FIFO_EN 0x00000001 + +// MB_LENGTH +#define FLD_MB_IF_LN_LNGTH 0x00000FFF + +// MB_HCMD register +#define FLD_MB_HCMD_H_GO 0x80000000 +#define FLD_MB_HCMD_H_BUSY 0x40000000 +#define FLD_MB_HCMD_H_DMA_HOLD 0x10000000 +#define FLD_MB_HCMD_H_DMA_BUSY 0x08000000 +#define FLD_MB_HCMD_H_DMA_TYPE 0x04000000 +#define FLD_MB_HCMD_H_DMA_XACT 0x02000000 +#define FLD_MB_HCMD_H_RW_N 0x01000000 +#define FLD_MB_HCMD_H_ADDR 0x00FF0000 +#define FLD_MB_HCMD_H_DATA 0x0000FFFF + +//***************************************************************************** +// I2C #1 +//***************************************************************************** +#define I2C1_ADDR 0x180000 // I2C #1 address +#define FLD_I2C_DADDR 0xfe000000 // RW [31:25] I2C Device Address + // RO [24] reserved +//***************************************************************************** +#define FLD_I2C_SADDR 0x00FFFFFF // RW [23:0] I2C Sub-address + +//***************************************************************************** +#define I2C1_WDATA 0x180004 // I2C #1 write data +#define FLD_I2C_WDATA 0xFFFFFFFF // RW [31:0] + +//***************************************************************************** +#define I2C1_CTRL 0x180008 // I2C #1 control +#define FLD_I2C_PERIOD 0xFF000000 // RW [31:24] +#define FLD_I2C_SCL_IN 0x00200000 // RW [21] +#define FLD_I2C_SDA_IN 0x00100000 // RW [20] + // RO [19:18] reserved +#define FLD_I2C_SCL_OUT 0x00020000 // RW [17] +#define FLD_I2C_SDA_OUT 0x00010000 // RW [16] + // RO [15] reserved +#define FLD_I2C_DATA_LEN 0x00007000 // RW [14:12] +#define FLD_I2C_SADDR_INC 0x00000800 // RW [11] + // RO [10:9] reserved +#define FLD_I2C_SADDR_LEN 0x00000300 // RW [9:8] + // RO [7:6] reserved +#define FLD_I2C_SOFT 0x00000020 // RW [5] +#define FLD_I2C_NOSTOP 0x00000010 // RW [4] +#define FLD_I2C_EXTEND 0x00000008 // RW [3] +#define FLD_I2C_SYNC 0x00000004 // RW [2] +#define FLD_I2C_READ_SA 0x00000002 // RW [1] +#define FLD_I2C_READ_WRN 0x00000001 // RW [0] + +//***************************************************************************** +#define I2C1_RDATA 0x18000C // I2C #1 read data +#define FLD_I2C_RDATA 0xFFFFFFFF // RO [31:0] + +//***************************************************************************** +#define I2C1_STAT 0x180010 // I2C #1 status +#define FLD_I2C_XFER_IN_PROG 0x00000002 // RO [1] +#define FLD_I2C_RACK 0x00000001 // RO [0] + +//***************************************************************************** +// I2C #2 +//***************************************************************************** +#define I2C2_ADDR 0x190000 // I2C #2 address + +//***************************************************************************** +#define I2C2_WDATA 0x190004 // I2C #2 write data + +//***************************************************************************** +#define I2C2_CTRL 0x190008 // I2C #2 control + +//***************************************************************************** +#define I2C2_RDATA 0x19000C // I2C #2 read data + +//***************************************************************************** +#define I2C2_STAT 0x190010 // I2C #2 status + +//***************************************************************************** +// I2C #3 +//***************************************************************************** +#define I2C3_ADDR 0x1A0000 // I2C #3 address + +//***************************************************************************** +#define I2C3_WDATA 0x1A0004 // I2C #3 write data + +//***************************************************************************** +#define I2C3_CTRL 0x1A0008 // I2C #3 control + +//***************************************************************************** +#define I2C3_RDATA 0x1A000C // I2C #3 read data + +//***************************************************************************** +#define I2C3_STAT 0x1A0010 // I2C #3 status + +//***************************************************************************** +// UART +//***************************************************************************** +#define UART_CTL 0x1B0000 // UART Control Register +#define FLD_LOOP_BACK_EN (1 << 7) // RW field - default 0 +#define FLD_RX_TRG_SZ (3 << 2) // RW field - default 0 +#define FLD_RX_EN (1 << 1) // RW field - default 0 +#define FLD_TX_EN (1 << 0) // RW field - default 0 + +//***************************************************************************** +#define UART_BRD 0x1B0004 // UART Baud Rate Divisor +#define FLD_BRD 0x0000FFFF // RW field - default 0x197 + +//***************************************************************************** +#define UART_DBUF 0x1B0008 // UART Tx/Rx Data BuFFer +#define FLD_DB 0xFFFFFFFF // RW field - default 0 + +//***************************************************************************** +#define UART_ISR 0x1B000C // UART Interrupt Status +#define FLD_RXD_TIMEOUT_EN (1 << 7) // RW field - default 0 +#define FLD_FRM_ERR_EN (1 << 6) // RW field - default 0 +#define FLD_RXD_RDY_EN (1 << 5) // RW field - default 0 +#define FLD_TXD_EMPTY_EN (1 << 4) // RW field - default 0 +#define FLD_RXD_OVERFLOW (1 << 3) // RW field - default 0 +#define FLD_FRM_ERR (1 << 2) // RW field - default 0 +#define FLD_RXD_RDY (1 << 1) // RW field - default 0 +#define FLD_TXD_EMPTY (1 << 0) // RW field - default 0 + +//***************************************************************************** +#define UART_CNT 0x1B0010 // UART Tx/Rx FIFO Byte Count +#define FLD_TXD_CNT (0x1F << 8) // RW field - default 0 +#define FLD_RXD_CNT (0x1F << 0) // RW field - default 0 + +//***************************************************************************** +// Motion Detection +#define MD_CH0_GRID_BLOCK_YCNT 0x170014 +#define MD_CH1_GRID_BLOCK_YCNT 0x170094 +#define MD_CH2_GRID_BLOCK_YCNT 0x170114 +#define MD_CH3_GRID_BLOCK_YCNT 0x170194 +#define MD_CH4_GRID_BLOCK_YCNT 0x170214 +#define MD_CH5_GRID_BLOCK_YCNT 0x170294 +#define MD_CH6_GRID_BLOCK_YCNT 0x170314 +#define MD_CH7_GRID_BLOCK_YCNT 0x170394 + +#define PIXEL_FRMT_422 4 +#define PIXEL_FRMT_411 5 +#define PIXEL_FRMT_Y8 6 + +#define PIXEL_ENGINE_VIP1 0 +#define PIXEL_ENGINE_VIP2 1 + +#endif //Athena_REGISTERS diff --git a/linux/drivers/staging/cx25821/cx25821-sram.h b/linux/drivers/staging/cx25821/cx25821-sram.h new file mode 100644 index 000000000..bd677ee22 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-sram.h @@ -0,0 +1,261 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ATHENA_SRAM_H__ +#define __ATHENA_SRAM_H__ + +//#define RX_SRAM_START_SIZE = 0; // Start of reserved SRAM +#define VID_CMDS_SIZE 80 // Video CMDS size in bytes +#define AUDIO_CMDS_SIZE 80 // AUDIO CMDS size in bytes +#define MBIF_CMDS_SIZE 80 // MBIF CMDS size in bytes + +//#define RX_SRAM_POOL_START_SIZE = 0; // Start of useable RX SRAM for buffers +#define VID_IQ_SIZE 64 // VID instruction queue size in bytes +#define MBIF_IQ_SIZE 64 +#define AUDIO_IQ_SIZE 64 // AUD instruction queue size in bytes + +#define VID_CDT_SIZE 64 // VID cluster descriptor table size in bytes +#define MBIF_CDT_SIZE 64 // MBIF/HBI cluster descriptor table size in bytes +#define AUDIO_CDT_SIZE 48 // AUD cluster descriptor table size in bytes + +//#define RX_SRAM_POOL_FREE_SIZE = 16; // Start of available RX SRAM +//#define RX_SRAM_END_SIZE = 0; // End of RX SRAM + +//#define TX_SRAM_POOL_START_SIZE = 0; // Start of transmit pool SRAM +//#define MSI_DATA_SIZE = 64; // Reserved (MSI Data, RISC working stora + +#define VID_CLUSTER_SIZE 1440 // VID cluster data line +#define AUDIO_CLUSTER_SIZE 128 // AUDIO cluster data line +#define MBIF_CLUSTER_SIZE 1440 // MBIF/HBI cluster data line + +//#define TX_SRAM_POOL_FREE_SIZE = 704; // Start of available TX SRAM +//#define TX_SRAM_END_SIZE = 0; // End of TX SRAM + +// Receive SRAM +#define RX_SRAM_START 0x10000 +#define VID_A_DOWN_CMDS 0x10000 +#define VID_B_DOWN_CMDS 0x10050 +#define VID_C_DOWN_CMDS 0x100A0 +#define VID_D_DOWN_CMDS 0x100F0 +#define VID_E_DOWN_CMDS 0x10140 +#define VID_F_DOWN_CMDS 0x10190 +#define VID_G_DOWN_CMDS 0x101E0 +#define VID_H_DOWN_CMDS 0x10230 +#define VID_A_UP_CMDS 0x10280 +#define VID_B_UP_CMDS 0x102D0 +#define VID_C_UP_CMDS 0x10320 +#define VID_D_UP_CMDS 0x10370 +#define VID_E_UP_CMDS 0x103C0 +#define VID_F_UP_CMDS 0x10410 +#define VID_I_UP_CMDS 0x10460 +#define VID_J_UP_CMDS 0x104B0 +#define AUD_A_DOWN_CMDS 0x10500 +#define AUD_B_DOWN_CMDS 0x10550 +#define AUD_C_DOWN_CMDS 0x105A0 +#define AUD_D_DOWN_CMDS 0x105F0 +#define AUD_A_UP_CMDS 0x10640 +#define AUD_B_UP_CMDS 0x10690 +#define AUD_C_UP_CMDS 0x106E0 +#define AUD_E_UP_CMDS 0x10730 +#define MBIF_A_DOWN_CMDS 0x10780 +#define MBIF_B_DOWN_CMDS 0x107D0 +#define DMA_SCRATCH_PAD 0x10820 // Scratch pad area from 0x10820 to 0x10B40 + +//#define RX_SRAM_POOL_START = 0x105B0; + +#define VID_A_IQ 0x11000 +#define VID_B_IQ 0x11040 +#define VID_C_IQ 0x11080 +#define VID_D_IQ 0x110C0 +#define VID_E_IQ 0x11100 +#define VID_F_IQ 0x11140 +#define VID_G_IQ 0x11180 +#define VID_H_IQ 0x111C0 +#define VID_I_IQ 0x11200 +#define VID_J_IQ 0x11240 +#define AUD_A_IQ 0x11280 +#define AUD_B_IQ 0x112C0 +#define AUD_C_IQ 0x11300 +#define AUD_D_IQ 0x11340 +#define AUD_E_IQ 0x11380 +#define MBIF_A_IQ 0x11000 +#define MBIF_B_IQ 0x110C0 + +#define VID_A_CDT 0x10C00 +#define VID_B_CDT 0x10C40 +#define VID_C_CDT 0x10C80 +#define VID_D_CDT 0x10CC0 +#define VID_E_CDT 0x10D00 +#define VID_F_CDT 0x10D40 +#define VID_G_CDT 0x10D80 +#define VID_H_CDT 0x10DC0 +#define VID_I_CDT 0x10E00 +#define VID_J_CDT 0x10E40 +#define AUD_A_CDT 0x10E80 +#define AUD_B_CDT 0x10EB0 +#define AUD_C_CDT 0x10EE0 +#define AUD_D_CDT 0x10F10 +#define AUD_E_CDT 0x10F40 +#define MBIF_A_CDT 0x10C00 +#define MBIF_B_CDT 0x10CC0 + +// Cluster Buffer for RX +#define VID_A_UP_CLUSTER_1 0x11400 +#define VID_A_UP_CLUSTER_2 0x119A0 +#define VID_A_UP_CLUSTER_3 0x11F40 +#define VID_A_UP_CLUSTER_4 0x124E0 + +#define VID_B_UP_CLUSTER_1 0x12A80 +#define VID_B_UP_CLUSTER_2 0x13020 +#define VID_B_UP_CLUSTER_3 0x135C0 +#define VID_B_UP_CLUSTER_4 0x13B60 + +#define VID_C_UP_CLUSTER_1 0x14100 +#define VID_C_UP_CLUSTER_2 0x146A0 +#define VID_C_UP_CLUSTER_3 0x14C40 +#define VID_C_UP_CLUSTER_4 0x151E0 + +#define VID_D_UP_CLUSTER_1 0x15780 +#define VID_D_UP_CLUSTER_2 0x15D20 +#define VID_D_UP_CLUSTER_3 0x162C0 +#define VID_D_UP_CLUSTER_4 0x16860 + +#define VID_E_UP_CLUSTER_1 0x16E00 +#define VID_E_UP_CLUSTER_2 0x173A0 +#define VID_E_UP_CLUSTER_3 0x17940 +#define VID_E_UP_CLUSTER_4 0x17EE0 + +#define VID_F_UP_CLUSTER_1 0x18480 +#define VID_F_UP_CLUSTER_2 0x18A20 +#define VID_F_UP_CLUSTER_3 0x18FC0 +#define VID_F_UP_CLUSTER_4 0x19560 + +#define VID_I_UP_CLUSTER_1 0x19B00 +#define VID_I_UP_CLUSTER_2 0x1A0A0 +#define VID_I_UP_CLUSTER_3 0x1A640 +#define VID_I_UP_CLUSTER_4 0x1ABE0 + +#define VID_J_UP_CLUSTER_1 0x1B180 +#define VID_J_UP_CLUSTER_2 0x1B720 +#define VID_J_UP_CLUSTER_3 0x1BCC0 +#define VID_J_UP_CLUSTER_4 0x1C260 + +#define AUD_A_UP_CLUSTER_1 0x1C800 +#define AUD_A_UP_CLUSTER_2 0x1C880 +#define AUD_A_UP_CLUSTER_3 0x1C900 + +#define AUD_B_UP_CLUSTER_1 0x1C980 +#define AUD_B_UP_CLUSTER_2 0x1CA00 +#define AUD_B_UP_CLUSTER_3 0x1CA80 + +#define AUD_C_UP_CLUSTER_1 0x1CB00 +#define AUD_C_UP_CLUSTER_2 0x1CB80 +#define AUD_C_UP_CLUSTER_3 0x1CC00 + +#define AUD_E_UP_CLUSTER_1 0x1CC80 +#define AUD_E_UP_CLUSTER_2 0x1CD00 +#define AUD_E_UP_CLUSTER_3 0x1CD80 + +#define RX_SRAM_POOL_FREE 0x1CE00 +#define RX_SRAM_END 0x1D000 + +// Free Receive SRAM 144 Bytes + +// Transmit SRAM +#define TX_SRAM_POOL_START 0x00000 + +#define VID_A_DOWN_CLUSTER_1 0x00040 +#define VID_A_DOWN_CLUSTER_2 0x005E0 +#define VID_A_DOWN_CLUSTER_3 0x00B80 +#define VID_A_DOWN_CLUSTER_4 0x01120 + +#define VID_B_DOWN_CLUSTER_1 0x016C0 +#define VID_B_DOWN_CLUSTER_2 0x01C60 +#define VID_B_DOWN_CLUSTER_3 0x02200 +#define VID_B_DOWN_CLUSTER_4 0x027A0 + +#define VID_C_DOWN_CLUSTER_1 0x02D40 +#define VID_C_DOWN_CLUSTER_2 0x032E0 +#define VID_C_DOWN_CLUSTER_3 0x03880 +#define VID_C_DOWN_CLUSTER_4 0x03E20 + +#define VID_D_DOWN_CLUSTER_1 0x043C0 +#define VID_D_DOWN_CLUSTER_2 0x04960 +#define VID_D_DOWN_CLUSTER_3 0x04F00 +#define VID_D_DOWN_CLUSTER_4 0x054A0 + +#define VID_E_DOWN_CLUSTER_1 0x05a40 +#define VID_E_DOWN_CLUSTER_2 0x05FE0 +#define VID_E_DOWN_CLUSTER_3 0x06580 +#define VID_E_DOWN_CLUSTER_4 0x06B20 + +#define VID_F_DOWN_CLUSTER_1 0x070C0 +#define VID_F_DOWN_CLUSTER_2 0x07660 +#define VID_F_DOWN_CLUSTER_3 0x07C00 +#define VID_F_DOWN_CLUSTER_4 0x081A0 + +#define VID_G_DOWN_CLUSTER_1 0x08740 +#define VID_G_DOWN_CLUSTER_2 0x08CE0 +#define VID_G_DOWN_CLUSTER_3 0x09280 +#define VID_G_DOWN_CLUSTER_4 0x09820 + +#define VID_H_DOWN_CLUSTER_1 0x09DC0 +#define VID_H_DOWN_CLUSTER_2 0x0A360 +#define VID_H_DOWN_CLUSTER_3 0x0A900 +#define VID_H_DOWN_CLUSTER_4 0x0AEA0 + +#define AUD_A_DOWN_CLUSTER_1 0x0B500 +#define AUD_A_DOWN_CLUSTER_2 0x0B580 +#define AUD_A_DOWN_CLUSTER_3 0x0B600 + +#define AUD_B_DOWN_CLUSTER_1 0x0B680 +#define AUD_B_DOWN_CLUSTER_2 0x0B700 +#define AUD_B_DOWN_CLUSTER_3 0x0B780 + +#define AUD_C_DOWN_CLUSTER_1 0x0B800 +#define AUD_C_DOWN_CLUSTER_2 0x0B880 +#define AUD_C_DOWN_CLUSTER_3 0x0B900 + +#define AUD_D_DOWN_CLUSTER_1 0x0B980 +#define AUD_D_DOWN_CLUSTER_2 0x0BA00 +#define AUD_D_DOWN_CLUSTER_3 0x0BA80 + +#define TX_SRAM_POOL_FREE 0x0BB00 +#define TX_SRAM_END 0x0C000 + +#define BYTES_TO_DWORDS(bcount) ((bcount) >> 2) +#define BYTES_TO_QWORDS(bcount) ((bcount) >> 3) +#define BYTES_TO_OWORDS(bcount) ((bcount) >> 4) + +#define VID_IQ_SIZE_DW BYTES_TO_DWORDS(VID_IQ_SIZE) +#define VID_CDT_SIZE_QW BYTES_TO_QWORDS(VID_CDT_SIZE) +#define VID_CLUSTER_SIZE_OW BYTES_TO_OWORDS(VID_CLUSTER_SIZE) + +#define AUDIO_IQ_SIZE_DW BYTES_TO_DWORDS(AUDIO_IQ_SIZE) +#define AUDIO_CDT_SIZE_QW BYTES_TO_QWORDS(AUDIO_CDT_SIZE) +#define AUDIO_CLUSTER_SIZE_QW BYTES_TO_QWORDS(AUDIO_CLUSTER_SIZE) + +#define MBIF_IQ_SIZE_DW BYTES_TO_DWORDS(MBIF_IQ_SIZE) +#define MBIF_CDT_SIZE_QW BYTES_TO_QWORDS(MBIF_CDT_SIZE) +#define MBIF_CLUSTER_SIZE_OW BYTES_TO_OWORDS(MBIF_CLUSTER_SIZE) + +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-video-upstream-ch2.c b/linux/drivers/staging/cx25821/cx25821-video-upstream-ch2.c new file mode 100644 index 000000000..c8905e0ac --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video-upstream-ch2.c @@ -0,0 +1,835 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" +#include "cx25821-video-upstream-ch2.h" + +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <asm/uaccess.h> + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); +MODULE_LICENSE("GPL"); + +static int _intr_msk = + FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR; + +static __le32 *cx25821_update_riscprogram_ch2(struct cx25821_dev *dev, + __le32 * rp, unsigned int offset, + unsigned int bpl, u32 sync_line, + unsigned int lines, + int fifo_enable, int field_type) +{ + unsigned int line, i; + int dist_betwn_starts = bpl * 2; + + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) { + *(rp++) = cpu_to_le32(RISC_NOOP); + } + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr_ch2 + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) + || !(dev->_isNTSC_ch2)) { + offset += dist_betwn_starts; + } + } + + return rp; +} + +static __le32 *cx25821_risc_field_upstream_ch2(struct cx25821_dev *dev, + __le32 * rp, + dma_addr_t databuf_phys_addr, + unsigned int offset, + u32 sync_line, unsigned int bpl, + unsigned int lines, + int fifo_enable, int field_type) +{ + unsigned int line, i; + struct sram_channel *sram_ch = + &dev->sram_channels[dev->_channel2_upstream_select]; + int dist_betwn_starts = bpl * 2; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) { + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + } + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) { + *(rp++) = cpu_to_le32(RISC_NOOP); + } + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(databuf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) + || !(dev->_isNTSC_ch2)) { + offset += dist_betwn_starts; + } + + // check if we need to enable the FIFO after the first 4 lines + // For the upstream video channel, the risc engine will enable the FIFO. + if (fifo_enable && line == 3) { + *(rp++) = RISC_WRITECR; + *(rp++) = sram_ch->dma_ctl; + *(rp++) = FLD_VID_FIFO_EN; + *(rp++) = 0x00000001; + } + } + + return rp; +} + +int cx25821_risc_buffer_upstream_ch2(struct cx25821_dev *dev, + struct pci_dev *pci, + unsigned int top_offset, unsigned int bpl, + unsigned int lines) +{ + __le32 *rp; + int fifo_enable = 0; + int singlefield_lines = lines >> 1; //get line count for single field + int odd_num_lines = singlefield_lines; + int frame = 0; + int frame_size = 0; + int databuf_offset = 0; + int risc_program_size = 0; + int risc_flag = RISC_CNT_RESET; + unsigned int bottom_offset = bpl; + dma_addr_t risc_phys_jump_addr; + + if (dev->_isNTSC_ch2) { + odd_num_lines = singlefield_lines + 1; + risc_program_size = FRAME1_VID_PROG_SIZE; + frame_size = + (bpl == + Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : + FRAME_SIZE_NTSC_Y422; + } else { + risc_program_size = PAL_VID_PROG_SIZE; + frame_size = + (bpl == + Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; + } + + /* Virtual address of Risc buffer program */ + rp = dev->_dma_virt_addr_ch2; + + for (frame = 0; frame < NUM_FRAMES; frame++) { + databuf_offset = frame_size * frame; + + if (UNSET != top_offset) { + fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; + rp = cx25821_risc_field_upstream_ch2(dev, rp, + dev-> + _data_buf_phys_addr_ch2 + + databuf_offset, + top_offset, 0, bpl, + odd_num_lines, + fifo_enable, + ODD_FIELD); + } + + fifo_enable = FIFO_DISABLE; + + //Even field + rp = cx25821_risc_field_upstream_ch2(dev, rp, + dev-> + _data_buf_phys_addr_ch2 + + databuf_offset, + bottom_offset, 0x200, bpl, + singlefield_lines, + fifo_enable, EVEN_FIELD); + + if (frame == 0) { + risc_flag = RISC_CNT_RESET; + risc_phys_jump_addr = + dev->_dma_phys_start_addr_ch2 + risc_program_size; + } else { + risc_flag = RISC_CNT_INC; + risc_phys_jump_addr = dev->_dma_phys_start_addr_ch2; + } + + // Loop to 2ndFrameRISC or to Start of Risc program & generate IRQ + *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + + return 0; +} + +void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev) +{ + struct sram_channel *sram_ch = + &dev->sram_channels[VID_UPSTREAM_SRAM_CHANNEL_J]; + u32 tmp = 0; + + if (!dev->_is_running_ch2) { + printk + ("cx25821: No video file is currently running so return!\n"); + return; + } + //Disable RISC interrupts + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp & ~_intr_msk); + + //Turn OFF risc and fifo + tmp = cx_read(sram_ch->dma_ctl); + cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); + + //Clear data buffer memory + if (dev->_data_buf_virt_addr_ch2) + memset(dev->_data_buf_virt_addr_ch2, 0, + dev->_data_buf_size_ch2); + + dev->_is_running_ch2 = 0; + dev->_is_first_frame_ch2 = 0; + dev->_frame_count_ch2 = 0; + dev->_file_status_ch2 = END_OF_FILE; + + if (dev->_irq_queues_ch2) { + kfree(dev->_irq_queues_ch2); + dev->_irq_queues_ch2 = NULL; + } + + if (dev->_filename_ch2 != NULL) + kfree(dev->_filename_ch2); + + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); +} + +void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev) +{ + if (dev->_is_running_ch2) { + cx25821_stop_upstream_video_ch2(dev); + } + + if (dev->_dma_virt_addr_ch2) { + pci_free_consistent(dev->pci, dev->_risc_size_ch2, + dev->_dma_virt_addr_ch2, + dev->_dma_phys_addr_ch2); + dev->_dma_virt_addr_ch2 = NULL; + } + + if (dev->_data_buf_virt_addr_ch2) { + pci_free_consistent(dev->pci, dev->_data_buf_size_ch2, + dev->_data_buf_virt_addr_ch2, + dev->_data_buf_phys_addr_ch2); + dev->_data_buf_virt_addr_ch2 = NULL; + } +} + +int cx25821_get_frame_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int frame_index_temp = dev->_frame_index_ch2; + int i = 0; + int line_size = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + int frame_size = 0; + int frame_offset = 0; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t file_offset; + loff_t pos; + mm_segment_t old_fs; + + if (dev->_file_status_ch2 == END_OF_FILE) + return 0; + + if (dev->_isNTSC_ch2) { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : + FRAME_SIZE_NTSC_Y422; + } else { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; + } + + frame_offset = (frame_index_temp > 0) ? frame_size : 0; + file_offset = dev->_frame_count_ch2 * frame_size; + + myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + printk("%s(): ERROR opening file(%s) with errno = %d! \n", + __func__, dev->_filename_ch2, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + printk("%s: File has no file operations registered!", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + printk("%s: File has no READ operations registered!", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (i = 0; i < dev->_lines_count_ch2; i++) { + pos = file_offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 && vfs_read_retval == line_size + && dev->_data_buf_virt_addr_ch2 != NULL) { + memcpy((void *)(dev->_data_buf_virt_addr_ch2 + + frame_offset / 4), mybuf, + vfs_read_retval); + } + + file_offset += vfs_read_retval; + frame_offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + printk(KERN_INFO + "Done: exit %s() since no more bytes to read from Video file.\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count_ch2++; + + dev->_file_status_ch2 = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + filp_close(myfile, NULL); + } + + return 0; +} + +static void cx25821_vidups_handler_ch2(struct work_struct *work) +{ + struct cx25821_dev *dev = + container_of(work, struct cx25821_dev, _irq_work_entry_ch2); + + if (!dev) { + printk("ERROR %s(): since container_of(work_struct) FAILED! \n", + __func__); + return; + } + + cx25821_get_frame_ch2(dev, + &dev->sram_channels[dev-> + _channel2_upstream_select]); +} + +int cx25821_openfile_ch2(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int i = 0, j = 0; + int line_size = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t pos; + loff_t offset = (unsigned long)0; + mm_segment_t old_fs; + + myfile = filp_open(dev->_filename_ch2, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + printk("%s(): ERROR opening file(%s) with errno = %d! \n", + __func__, dev->_filename_ch2, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + printk("%s: File has no file operations registered!", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + printk + ("%s: File has no READ operations registered! Returning.", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (j = 0; j < NUM_FRAMES; j++) { + for (i = 0; i < dev->_lines_count_ch2; i++) { + pos = offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 + && vfs_read_retval == line_size + && dev->_data_buf_virt_addr_ch2 != NULL) { + memcpy((void *)(dev-> + _data_buf_virt_addr_ch2 + + offset / 4), mybuf, + vfs_read_retval); + } + + offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + printk(KERN_INFO + "Done: exit %s() since no more bytes to read from Video file.\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count_ch2++; + + if (vfs_read_retval < line_size) { + break; + } + } + + dev->_file_status_ch2 = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + myfile->f_pos = 0; + filp_close(myfile, NULL); + } + + return 0; +} + +static int cx25821_upstream_buffer_prepare_ch2(struct cx25821_dev *dev, + struct sram_channel *sram_ch, + int bpl) +{ + int ret = 0; + dma_addr_t dma_addr; + dma_addr_t data_dma_addr; + + if (dev->_dma_virt_addr_ch2 != NULL) { + pci_free_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, + dev->_dma_virt_addr_ch2, + dev->_dma_phys_addr_ch2); + } + + dev->_dma_virt_addr_ch2 = + pci_alloc_consistent(dev->pci, dev->upstream_riscbuf_size_ch2, + &dma_addr); + dev->_dma_virt_start_addr_ch2 = dev->_dma_virt_addr_ch2; + dev->_dma_phys_start_addr_ch2 = dma_addr; + dev->_dma_phys_addr_ch2 = dma_addr; + dev->_risc_size_ch2 = dev->upstream_riscbuf_size_ch2; + + if (!dev->_dma_virt_addr_ch2) { + printk + ("cx25821: FAILED to allocate memory for Risc buffer! Returning.\n"); + return -ENOMEM; + } + + //Iniitize at this address until n bytes to 0 + memset(dev->_dma_virt_addr_ch2, 0, dev->_risc_size_ch2); + + if (dev->_data_buf_virt_addr_ch2 != NULL) { + pci_free_consistent(dev->pci, dev->upstream_databuf_size_ch2, + dev->_data_buf_virt_addr_ch2, + dev->_data_buf_phys_addr_ch2); + } + //For Video Data buffer allocation + dev->_data_buf_virt_addr_ch2 = + pci_alloc_consistent(dev->pci, dev->upstream_databuf_size_ch2, + &data_dma_addr); + dev->_data_buf_phys_addr_ch2 = data_dma_addr; + dev->_data_buf_size_ch2 = dev->upstream_databuf_size_ch2; + + if (!dev->_data_buf_virt_addr_ch2) { + printk + ("cx25821: FAILED to allocate memory for data buffer! Returning.\n"); + return -ENOMEM; + } + + //Initialize at this address until n bytes to 0 + memset(dev->_data_buf_virt_addr_ch2, 0, dev->_data_buf_size_ch2); + + ret = cx25821_openfile_ch2(dev, sram_ch); + if (ret < 0) + return ret; + + //Creating RISC programs + ret = + cx25821_risc_buffer_upstream_ch2(dev, dev->pci, 0, bpl, + dev->_lines_count_ch2); + if (ret < 0) { + printk(KERN_INFO + "cx25821: Failed creating Video Upstream Risc programs! \n"); + goto error; + } + + return 0; + + error: + return ret; +} + +int cx25821_video_upstream_irq_ch2(struct cx25821_dev *dev, int chan_num, + u32 status) +{ + u32 int_msk_tmp; + struct sram_channel *channel = &dev->sram_channels[chan_num]; + int singlefield_lines = NTSC_FIELD_HEIGHT; + int line_size_in_bytes = Y422_LINE_SZ; + int odd_risc_prog_size = 0; + dma_addr_t risc_phys_jump_addr; + __le32 *rp; + + if (status & FLD_VID_SRC_RISC1) { + // We should only process one program per call + u32 prog_cnt = cx_read(channel->gpcnt); + + //Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); + cx_write(channel->int_stat, _intr_msk); + + spin_lock(&dev->slock); + + dev->_frame_index_ch2 = prog_cnt; + + queue_work(dev->_irq_queues_ch2, &dev->_irq_work_entry_ch2); + + if (dev->_is_first_frame_ch2) { + dev->_is_first_frame_ch2 = 0; + + if (dev->_isNTSC_ch2) { + singlefield_lines += 1; + odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; + } else { + singlefield_lines = PAL_FIELD_HEIGHT; + odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; + } + + if (dev->_dma_virt_start_addr_ch2 != NULL) { + line_size_in_bytes = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? Y411_LINE_SZ : + Y422_LINE_SZ; + risc_phys_jump_addr = + dev->_dma_phys_start_addr_ch2 + + odd_risc_prog_size; + + rp = cx25821_update_riscprogram_ch2(dev, + dev-> + _dma_virt_start_addr_ch2, + TOP_OFFSET, + line_size_in_bytes, + 0x0, + singlefield_lines, + FIFO_DISABLE, + ODD_FIELD); + + // Jump to Even Risc program of 1st Frame + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + } + + spin_unlock(&dev->slock); + } + + if (dev->_file_status_ch2 == END_OF_FILE) { + printk("cx25821: EOF Channel 2 Framecount = %d\n", + dev->_frame_count_ch2); + return -1; + } + //ElSE, set the interrupt mask register, re-enable irq. + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); + + return 0; +} + +static irqreturn_t cx25821_upstream_irq_ch2(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 msk_stat, vid_status; + int handled = 0; + int channel_num = 0; + struct sram_channel *sram_ch; + + if (!dev) + return -1; + + channel_num = VID_UPSTREAM_SRAM_CHANNEL_J; + + sram_ch = &dev->sram_channels[channel_num]; + + msk_stat = cx_read(sram_ch->int_mstat); + vid_status = cx_read(sram_ch->int_stat); + + // Only deal with our interrupt + if (vid_status) { + handled = + cx25821_video_upstream_irq_ch2(dev, channel_num, + vid_status); + } + + if (handled < 0) { + cx25821_stop_upstream_video_ch2(dev); + } else { + handled += handled; + } + + return IRQ_RETVAL(handled); +} + +static void cx25821_set_pixelengine_ch2(struct cx25821_dev *dev, + struct sram_channel *ch, int pix_format) +{ + int width = WIDTH_D1; + int height = dev->_lines_count_ch2; + int num_lines, odd_num_lines; + u32 value; + int vip_mode = PIXEL_ENGINE_VIP1; + + value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); + value &= 0xFFFFFFEF; + value |= dev->_isNTSC_ch2 ? 0 : 0x10; + cx_write(ch->vid_fmt_ctl, value); + + // set number of active pixels in each line. Default is 720 pixels in both NTSC and PAL format + cx_write(ch->vid_active_ctl1, width); + + num_lines = (height / 2) & 0x3FF; + odd_num_lines = num_lines; + + if (dev->_isNTSC_ch2) { + odd_num_lines += 1; + } + + value = (num_lines << 16) | odd_num_lines; + + // set number of active lines in field 0 (top) and field 1 (bottom) + cx_write(ch->vid_active_ctl2, value); + + cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); +} + +int cx25821_start_video_dma_upstream_ch2(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + u32 tmp = 0; + int err = 0; + + // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + // Set the physical start address of the RISC program in the initial program counter(IPC) member of the cmds. + cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr_ch2); + cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ + + /* reset counter */ + cx_write(sram_ch->gpcnt_ctl, 3); + + // Clear our bits from the interrupt status register. + cx_write(sram_ch->int_stat, _intr_msk); + + //Set the interrupt mask register, enable irq. + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp |= _intr_msk); + + err = + request_irq(dev->pci->irq, cx25821_upstream_irq_ch2, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (err < 0) { + printk(KERN_ERR "%s: can't get upstream IRQ %d\n", dev->name, + dev->pci->irq); + goto fail_irq; + } + // Start the DMA engine + tmp = cx_read(sram_ch->dma_ctl); + cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); + + dev->_is_running_ch2 = 1; + dev->_is_first_frame_ch2 = 1; + + return 0; + + fail_irq: + cx25821_dev_unregister(dev); + return err; +} + +int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, int channel_select, + int pixel_format) +{ + struct sram_channel *sram_ch; + u32 tmp; + int retval = 0; + int err = 0; + int data_frame_size = 0; + int risc_buffer_size = 0; + int str_length = 0; + + if (dev->_is_running_ch2) { + printk("Video Channel is still running so return!\n"); + return 0; + } + + dev->_channel2_upstream_select = channel_select; + sram_ch = &dev->sram_channels[channel_select]; + + INIT_WORK(&dev->_irq_work_entry_ch2, cx25821_vidups_handler_ch2); + dev->_irq_queues_ch2 = + create_singlethread_workqueue("cx25821_workqueue2"); + + if (!dev->_irq_queues_ch2) { + printk + ("cx25821: create_singlethread_workqueue() for Video FAILED!\n"); + return -ENOMEM; + } + // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + dev->_is_running_ch2 = 0; + dev->_frame_count_ch2 = 0; + dev->_file_status_ch2 = RESET_STATUS; + dev->_lines_count_ch2 = dev->_isNTSC_ch2 ? 480 : 576; + dev->_pixel_format_ch2 = pixel_format; + dev->_line_size_ch2 = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; + data_frame_size = dev->_isNTSC_ch2 ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; + risc_buffer_size = + dev->_isNTSC_ch2 ? NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; + + if (dev->input_filename_ch2) { + str_length = strlen(dev->input_filename_ch2); + dev->_filename_ch2 = + (char *)kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename_ch2) + goto error; + + memcpy(dev->_filename_ch2, dev->input_filename_ch2, + str_length + 1); + } else { + str_length = strlen(dev->_defaultname_ch2); + dev->_filename_ch2 = + (char *)kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename_ch2) + goto error; + + memcpy(dev->_filename_ch2, dev->_defaultname_ch2, + str_length + 1); + } + + //Default if filename is empty string + if (strcmp(dev->input_filename_ch2, "") == 0) { + if (dev->_isNTSC_ch2) { + dev->_filename_ch2 = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? "/root/vid411.yuv" : + "/root/vidtest.yuv"; + } else { + dev->_filename_ch2 = + (dev->_pixel_format_ch2 == + PIXEL_FRMT_411) ? "/root/pal411.yuv" : + "/root/pal422.yuv"; + } + } + + retval = + cx25821_sram_channel_setup_upstream(dev, sram_ch, + dev->_line_size_ch2, 0); + + /* setup fifo + format */ + cx25821_set_pixelengine_ch2(dev, sram_ch, dev->_pixel_format_ch2); + + dev->upstream_riscbuf_size_ch2 = risc_buffer_size * 2; + dev->upstream_databuf_size_ch2 = data_frame_size * 2; + + //Allocating buffers and prepare RISC program + retval = + cx25821_upstream_buffer_prepare_ch2(dev, sram_ch, + dev->_line_size_ch2); + if (retval < 0) { + printk(KERN_ERR + "%s: Failed to set up Video upstream buffers!\n", + dev->name); + goto error; + } + + cx25821_start_video_dma_upstream_ch2(dev, sram_ch); + + return 0; + + error: + cx25821_dev_unregister(dev); + + return err; +} diff --git a/linux/drivers/staging/cx25821/cx25821-video-upstream-ch2.h b/linux/drivers/staging/cx25821/cx25821-video-upstream-ch2.h new file mode 100644 index 000000000..79dfc8759 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video-upstream-ch2.h @@ -0,0 +1,102 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "compat.h" +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#define OPEN_FILE_1 0 +#define NUM_PROGS 8 +#define NUM_FRAMES 2 +#define ODD_FIELD 0 +#define EVEN_FIELD 1 +#define TOP_OFFSET 0 +#define FIFO_DISABLE 0 +#define FIFO_ENABLE 1 +#define TEST_FRAMES 5 +#define END_OF_FILE 0 +#define IN_PROGRESS 1 +#define RESET_STATUS -1 +#define NUM_NO_OPS 5 + +// PAL and NTSC line sizes and number of lines. +#define WIDTH_D1 720 +#define NTSC_LINES_PER_FRAME 480 +#define PAL_LINES_PER_FRAME 576 +#define PAL_LINE_SZ 1440 +#define Y422_LINE_SZ 1440 +#define Y411_LINE_SZ 1080 +#define NTSC_FIELD_HEIGHT 240 +#define NTSC_ODD_FLD_LINES 241 +#define PAL_FIELD_HEIGHT 288 + +#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) +#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) + +#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) +#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) + +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define JUMP_INSTRUCTION_SIZE 12 +#define MAXSIZE_NO_OPS 36 +#define DWORD_SIZE 4 + +#define USE_RISC_NOOP_VIDEO 1 + +#ifdef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) + +#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) + +#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE) + +#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) + +#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) + +#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) + +#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE) +#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) +#endif + +#ifndef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE ) +#define PAL_RISC_BUF_SIZE ( 2 * (RISC_SYNC_INSTRUCTION_SIZE + PAL_US_VID_PROG_SIZE) ) +#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE ) +#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE ) +#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE ) +#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) +#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE) ) +#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE ) +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-video-upstream.c b/linux/drivers/staging/cx25821/cx25821-video-upstream.c new file mode 100644 index 000000000..3d7dd3f66 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video-upstream.c @@ -0,0 +1,894 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" +#include "cx25821-video-upstream.h" + +#include <linux/fs.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/syscalls.h> +#include <linux/file.h> +#include <linux/fcntl.h> +#include <asm/uaccess.h> + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Hiep Huynh <hiep.huynh@conexant.com>"); +MODULE_LICENSE("GPL"); + +static int _intr_msk = + FLD_VID_SRC_RISC1 | FLD_VID_SRC_UF | FLD_VID_SRC_SYNC | FLD_VID_SRC_OPC_ERR; + +int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc) +{ + unsigned int i, lines; + u32 cdt; + + if (ch->cmds_start == 0) { + cx_write(ch->ptr1_reg, 0); + cx_write(ch->ptr2_reg, 0); + cx_write(ch->cnt2_reg, 0); + cx_write(ch->cnt1_reg, 0); + return 0; + } + + bpl = (bpl + 7) & ~7; /* alignment */ + cdt = ch->cdt; + lines = ch->fifo_size / bpl; + + if (lines > 4) { + lines = 4; + } + + BUG_ON(lines < 2); + + /* write CDT */ + for (i = 0; i < lines; i++) { + cx_write(cdt + 16 * i, ch->fifo_start + bpl * i); + cx_write(cdt + 16 * i + 4, 0); + cx_write(cdt + 16 * i + 8, 0); + cx_write(cdt + 16 * i + 12, 0); + } + + /* write CMDS */ + cx_write(ch->cmds_start + 0, risc); + + cx_write(ch->cmds_start + 4, 0); + cx_write(ch->cmds_start + 8, cdt); + cx_write(ch->cmds_start + 12, (lines * 16) >> 3); + cx_write(ch->cmds_start + 16, ch->ctrl_start); + + cx_write(ch->cmds_start + 20, VID_IQ_SIZE_DW); + + for (i = 24; i < 80; i += 4) + cx_write(ch->cmds_start + i, 0); + + /* fill registers */ + cx_write(ch->ptr1_reg, ch->fifo_start); + cx_write(ch->ptr2_reg, cdt); + cx_write(ch->cnt2_reg, (lines * 16) >> 3); + cx_write(ch->cnt1_reg, (bpl >> 3) - 1); + + return 0; +} + +static __le32 *cx25821_update_riscprogram(struct cx25821_dev *dev, + __le32 * rp, unsigned int offset, + unsigned int bpl, u32 sync_line, + unsigned int lines, int fifo_enable, + int field_type) +{ + unsigned int line, i; + int dist_betwn_starts = bpl * 2; + + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) { + *(rp++) = cpu_to_le32(RISC_NOOP); + } + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(dev->_data_buf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) { + offset += dist_betwn_starts; + } + } + + return rp; +} + +static __le32 *cx25821_risc_field_upstream(struct cx25821_dev *dev, __le32 * rp, + dma_addr_t databuf_phys_addr, + unsigned int offset, u32 sync_line, + unsigned int bpl, unsigned int lines, + int fifo_enable, int field_type) +{ + unsigned int line, i; + struct sram_channel *sram_ch = + &dev->sram_channels[dev->_channel_upstream_select]; + int dist_betwn_starts = bpl * 2; + + /* sync instruction */ + if (sync_line != NO_SYNC_LINE) { + *(rp++) = cpu_to_le32(RISC_RESYNC | sync_line); + } + + if (USE_RISC_NOOP_VIDEO) { + for (i = 0; i < NUM_NO_OPS; i++) { + *(rp++) = cpu_to_le32(RISC_NOOP); + } + } + + /* scan lines */ + for (line = 0; line < lines; line++) { + *(rp++) = cpu_to_le32(RISC_READ | RISC_SOL | RISC_EOL | bpl); + *(rp++) = cpu_to_le32(databuf_phys_addr + offset); + *(rp++) = cpu_to_le32(0); /* bits 63-32 */ + + if ((lines <= NTSC_FIELD_HEIGHT) + || (line < (NTSC_FIELD_HEIGHT - 1)) || !(dev->_isNTSC)) { + offset += dist_betwn_starts; //to skip the other field line + } + + // check if we need to enable the FIFO after the first 4 lines + // For the upstream video channel, the risc engine will enable the FIFO. + if (fifo_enable && line == 3) { + *(rp++) = RISC_WRITECR; + *(rp++) = sram_ch->dma_ctl; + *(rp++) = FLD_VID_FIFO_EN; + *(rp++) = 0x00000001; + } + } + + return rp; +} + +int cx25821_risc_buffer_upstream(struct cx25821_dev *dev, + struct pci_dev *pci, + unsigned int top_offset, + unsigned int bpl, unsigned int lines) +{ + __le32 *rp; + int fifo_enable = 0; + int singlefield_lines = lines >> 1; //get line count for single field + int odd_num_lines = singlefield_lines; + int frame = 0; + int frame_size = 0; + int databuf_offset = 0; + int risc_program_size = 0; + int risc_flag = RISC_CNT_RESET; + unsigned int bottom_offset = bpl; + dma_addr_t risc_phys_jump_addr; + + if (dev->_isNTSC) { + odd_num_lines = singlefield_lines + 1; + risc_program_size = FRAME1_VID_PROG_SIZE; + frame_size = + (bpl == + Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : + FRAME_SIZE_NTSC_Y422; + } else { + risc_program_size = PAL_VID_PROG_SIZE; + frame_size = + (bpl == + Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; + } + + /* Virtual address of Risc buffer program */ + rp = dev->_dma_virt_addr; + + for (frame = 0; frame < NUM_FRAMES; frame++) { + databuf_offset = frame_size * frame; + + if (UNSET != top_offset) { + fifo_enable = (frame == 0) ? FIFO_ENABLE : FIFO_DISABLE; + rp = cx25821_risc_field_upstream(dev, rp, + dev-> + _data_buf_phys_addr + + databuf_offset, + top_offset, 0, bpl, + odd_num_lines, + fifo_enable, + ODD_FIELD); + } + + fifo_enable = FIFO_DISABLE; + + //Even Field + rp = cx25821_risc_field_upstream(dev, rp, + dev->_data_buf_phys_addr + + databuf_offset, bottom_offset, + 0x200, bpl, singlefield_lines, + fifo_enable, EVEN_FIELD); + + if (frame == 0) { + risc_flag = RISC_CNT_RESET; + risc_phys_jump_addr = + dev->_dma_phys_start_addr + risc_program_size; + } else { + risc_phys_jump_addr = dev->_dma_phys_start_addr; + risc_flag = RISC_CNT_INC; + } + + // Loop to 2ndFrameRISC or to Start of Risc program & generate IRQ + *(rp++) = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | risc_flag); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + + return 0; +} + +void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev) +{ + struct sram_channel *sram_ch = + &dev->sram_channels[VID_UPSTREAM_SRAM_CHANNEL_I]; + u32 tmp = 0; + + if (!dev->_is_running) { + printk + ("cx25821: No video file is currently running so return!\n"); + return; + } + //Disable RISC interrupts + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp & ~_intr_msk); + + //Turn OFF risc and fifo enable + tmp = cx_read(sram_ch->dma_ctl); + cx_write(sram_ch->dma_ctl, tmp & ~(FLD_VID_FIFO_EN | FLD_VID_RISC_EN)); + + //Clear data buffer memory + if (dev->_data_buf_virt_addr) + memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); + + dev->_is_running = 0; + dev->_is_first_frame = 0; + dev->_frame_count = 0; + dev->_file_status = END_OF_FILE; + + if (dev->_irq_queues) { + kfree(dev->_irq_queues); + dev->_irq_queues = NULL; + } + + if (dev->_filename != NULL) + kfree(dev->_filename); + + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); +} + +void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev) +{ + if (dev->_is_running) { + cx25821_stop_upstream_video_ch1(dev); + } + + if (dev->_dma_virt_addr) { + pci_free_consistent(dev->pci, dev->_risc_size, + dev->_dma_virt_addr, dev->_dma_phys_addr); + dev->_dma_virt_addr = NULL; + } + + if (dev->_data_buf_virt_addr) { + pci_free_consistent(dev->pci, dev->_data_buf_size, + dev->_data_buf_virt_addr, + dev->_data_buf_phys_addr); + dev->_data_buf_virt_addr = NULL; + } +} + +int cx25821_get_frame(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int frame_index_temp = dev->_frame_index; + int i = 0; + int line_size = + (dev->_pixel_format == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + int frame_size = 0; + int frame_offset = 0; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t file_offset; + loff_t pos; + mm_segment_t old_fs; + + if (dev->_file_status == END_OF_FILE) + return 0; + + if (dev->_isNTSC) { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_NTSC_Y411 : + FRAME_SIZE_NTSC_Y422; + } else { + frame_size = + (line_size == + Y411_LINE_SZ) ? FRAME_SIZE_PAL_Y411 : FRAME_SIZE_PAL_Y422; + } + + frame_offset = (frame_index_temp > 0) ? frame_size : 0; + file_offset = dev->_frame_count * frame_size; + + myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + printk("%s(): ERROR opening file(%s) with errno = %d! \n", + __func__, dev->_filename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + printk("%s: File has no file operations registered!", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + printk("%s: File has no READ operations registered!", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (i = 0; i < dev->_lines_count; i++) { + pos = file_offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 && vfs_read_retval == line_size + && dev->_data_buf_virt_addr != NULL) { + memcpy((void *)(dev->_data_buf_virt_addr + + frame_offset / 4), mybuf, + vfs_read_retval); + } + + file_offset += vfs_read_retval; + frame_offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + printk(KERN_INFO + "Done: exit %s() since no more bytes to read from Video file.\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count++; + + dev->_file_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + filp_close(myfile, NULL); + } + + return 0; +} + +static void cx25821_vidups_handler(struct work_struct *work) +{ + struct cx25821_dev *dev = + container_of(work, struct cx25821_dev, _irq_work_entry); + + if (!dev) { + printk("ERROR %s(): since container_of(work_struct) FAILED! \n", + __func__); + return; + } + + cx25821_get_frame(dev, + &dev->sram_channels[dev->_channel_upstream_select]); +} + +int cx25821_openfile(struct cx25821_dev *dev, struct sram_channel *sram_ch) +{ + struct file *myfile; + int i = 0, j = 0; + int line_size = + (dev->_pixel_format == + PIXEL_FRMT_411) ? Y411_LINE_SZ : Y422_LINE_SZ; + ssize_t vfs_read_retval = 0; + char mybuf[line_size]; + loff_t pos; + loff_t offset = (unsigned long)0; + mm_segment_t old_fs; + + myfile = filp_open(dev->_filename, O_RDONLY | O_LARGEFILE, 0); + + if (IS_ERR(myfile)) { + const int open_errno = -PTR_ERR(myfile); + printk("%s(): ERROR opening file(%s) with errno = %d! \n", + __func__, dev->_filename, open_errno); + return PTR_ERR(myfile); + } else { + if (!(myfile->f_op)) { + printk("%s: File has no file operations registered!", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + if (!myfile->f_op->read) { + printk + ("%s: File has no READ operations registered! Returning.", + __func__); + filp_close(myfile, NULL); + return -EIO; + } + + pos = myfile->f_pos; + old_fs = get_fs(); + set_fs(KERNEL_DS); + + for (j = 0; j < NUM_FRAMES; j++) { + for (i = 0; i < dev->_lines_count; i++) { + pos = offset; + + vfs_read_retval = + vfs_read(myfile, mybuf, line_size, &pos); + + if (vfs_read_retval > 0 + && vfs_read_retval == line_size + && dev->_data_buf_virt_addr != NULL) { + memcpy((void *)(dev-> + _data_buf_virt_addr + + offset / 4), mybuf, + vfs_read_retval); + } + + offset += vfs_read_retval; + + if (vfs_read_retval < line_size) { + printk(KERN_INFO + "Done: exit %s() since no more bytes to read from Video file.\n", + __func__); + break; + } + } + + if (i > 0) + dev->_frame_count++; + + if (vfs_read_retval < line_size) { + break; + } + } + + dev->_file_status = + (vfs_read_retval == line_size) ? IN_PROGRESS : END_OF_FILE; + + set_fs(old_fs); + myfile->f_pos = 0; + filp_close(myfile, NULL); + } + + return 0; +} + +int cx25821_upstream_buffer_prepare(struct cx25821_dev *dev, + struct sram_channel *sram_ch, int bpl) +{ + int ret = 0; + dma_addr_t dma_addr; + dma_addr_t data_dma_addr; + + if (dev->_dma_virt_addr != NULL) { + pci_free_consistent(dev->pci, dev->upstream_riscbuf_size, + dev->_dma_virt_addr, dev->_dma_phys_addr); + } + + dev->_dma_virt_addr = + pci_alloc_consistent(dev->pci, dev->upstream_riscbuf_size, + &dma_addr); + dev->_dma_virt_start_addr = dev->_dma_virt_addr; + dev->_dma_phys_start_addr = dma_addr; + dev->_dma_phys_addr = dma_addr; + dev->_risc_size = dev->upstream_riscbuf_size; + + if (!dev->_dma_virt_addr) { + printk + ("cx25821: FAILED to allocate memory for Risc buffer! Returning.\n"); + return -ENOMEM; + } + + //Clear memory at address + memset(dev->_dma_virt_addr, 0, dev->_risc_size); + + if (dev->_data_buf_virt_addr != NULL) { + pci_free_consistent(dev->pci, dev->upstream_databuf_size, + dev->_data_buf_virt_addr, + dev->_data_buf_phys_addr); + } + //For Video Data buffer allocation + dev->_data_buf_virt_addr = + pci_alloc_consistent(dev->pci, dev->upstream_databuf_size, + &data_dma_addr); + dev->_data_buf_phys_addr = data_dma_addr; + dev->_data_buf_size = dev->upstream_databuf_size; + + if (!dev->_data_buf_virt_addr) { + printk + ("cx25821: FAILED to allocate memory for data buffer! Returning.\n"); + return -ENOMEM; + } + + //Clear memory at address + memset(dev->_data_buf_virt_addr, 0, dev->_data_buf_size); + + ret = cx25821_openfile(dev, sram_ch); + if (ret < 0) + return ret; + + //Create RISC programs + ret = + cx25821_risc_buffer_upstream(dev, dev->pci, 0, bpl, + dev->_lines_count); + if (ret < 0) { + printk(KERN_INFO + "cx25821: Failed creating Video Upstream Risc programs! \n"); + goto error; + } + + return 0; + + error: + return ret; +} + +int cx25821_video_upstream_irq(struct cx25821_dev *dev, int chan_num, + u32 status) +{ + u32 int_msk_tmp; + struct sram_channel *channel = &dev->sram_channels[chan_num]; + int singlefield_lines = NTSC_FIELD_HEIGHT; + int line_size_in_bytes = Y422_LINE_SZ; + int odd_risc_prog_size = 0; + dma_addr_t risc_phys_jump_addr; + __le32 *rp; + + if (status & FLD_VID_SRC_RISC1) { + // We should only process one program per call + u32 prog_cnt = cx_read(channel->gpcnt); + + //Since we've identified our IRQ, clear our bits from the interrupt mask and interrupt status registers + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp & ~_intr_msk); + cx_write(channel->int_stat, _intr_msk); + + spin_lock(&dev->slock); + + dev->_frame_index = prog_cnt; + + queue_work(dev->_irq_queues, &dev->_irq_work_entry); + + if (dev->_is_first_frame) { + dev->_is_first_frame = 0; + + if (dev->_isNTSC) { + singlefield_lines += 1; + odd_risc_prog_size = ODD_FLD_NTSC_PROG_SIZE; + } else { + singlefield_lines = PAL_FIELD_HEIGHT; + odd_risc_prog_size = ODD_FLD_PAL_PROG_SIZE; + } + + if (dev->_dma_virt_start_addr != NULL) { + line_size_in_bytes = + (dev->_pixel_format == + PIXEL_FRMT_411) ? Y411_LINE_SZ : + Y422_LINE_SZ; + risc_phys_jump_addr = + dev->_dma_phys_start_addr + + odd_risc_prog_size; + + rp = cx25821_update_riscprogram(dev, + dev-> + _dma_virt_start_addr, + TOP_OFFSET, + line_size_in_bytes, + 0x0, + singlefield_lines, + FIFO_DISABLE, + ODD_FIELD); + + // Jump to Even Risc program of 1st Frame + *(rp++) = cpu_to_le32(RISC_JUMP); + *(rp++) = cpu_to_le32(risc_phys_jump_addr); + *(rp++) = cpu_to_le32(0); + } + } + + spin_unlock(&dev->slock); + } else { + if (status & FLD_VID_SRC_UF) + printk + ("%s: Video Received Underflow Error Interrupt!\n", + __func__); + + if (status & FLD_VID_SRC_SYNC) + printk("%s: Video Received Sync Error Interrupt!\n", + __func__); + + if (status & FLD_VID_SRC_OPC_ERR) + printk("%s: Video Received OpCode Error Interrupt!\n", + __func__); + } + + if (dev->_file_status == END_OF_FILE) { + printk("cx25821: EOF Channel 1 Framecount = %d\n", + dev->_frame_count); + return -1; + } + //ElSE, set the interrupt mask register, re-enable irq. + int_msk_tmp = cx_read(channel->int_msk); + cx_write(channel->int_msk, int_msk_tmp |= _intr_msk); + + return 0; +} + +static irqreturn_t cx25821_upstream_irq(int irq, void *dev_id) +{ + struct cx25821_dev *dev = dev_id; + u32 msk_stat, vid_status; + int handled = 0; + int channel_num = 0; + struct sram_channel *sram_ch; + + if (!dev) + return -1; + + channel_num = VID_UPSTREAM_SRAM_CHANNEL_I; + + sram_ch = &dev->sram_channels[channel_num]; + + msk_stat = cx_read(sram_ch->int_mstat); + vid_status = cx_read(sram_ch->int_stat); + + // Only deal with our interrupt + if (vid_status) { + handled = + cx25821_video_upstream_irq(dev, channel_num, vid_status); + } + + if (handled < 0) { + cx25821_stop_upstream_video_ch1(dev); + } else { + handled += handled; + } + + return IRQ_RETVAL(handled); +} + +void cx25821_set_pixelengine(struct cx25821_dev *dev, struct sram_channel *ch, + int pix_format) +{ + int width = WIDTH_D1; + int height = dev->_lines_count; + int num_lines, odd_num_lines; + u32 value; + int vip_mode = OUTPUT_FRMT_656; + + value = ((pix_format & 0x3) << 12) | (vip_mode & 0x7); + value &= 0xFFFFFFEF; + value |= dev->_isNTSC ? 0 : 0x10; + cx_write(ch->vid_fmt_ctl, value); + + // set number of active pixels in each line. Default is 720 pixels in both NTSC and PAL format + cx_write(ch->vid_active_ctl1, width); + + num_lines = (height / 2) & 0x3FF; + odd_num_lines = num_lines; + + if (dev->_isNTSC) { + odd_num_lines += 1; + } + + value = (num_lines << 16) | odd_num_lines; + + // set number of active lines in field 0 (top) and field 1 (bottom) + cx_write(ch->vid_active_ctl2, value); + + cx_write(ch->vid_cdt_size, VID_CDT_SIZE >> 3); +} + +int cx25821_start_video_dma_upstream(struct cx25821_dev *dev, + struct sram_channel *sram_ch) +{ + u32 tmp = 0; + int err = 0; + + // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + // Set the physical start address of the RISC program in the initial program counter(IPC) member of the cmds. + cx_write(sram_ch->cmds_start + 0, dev->_dma_phys_addr); + cx_write(sram_ch->cmds_start + 4, 0); /* Risc IPC High 64 bits 63-32 */ + + /* reset counter */ + cx_write(sram_ch->gpcnt_ctl, 3); + + // Clear our bits from the interrupt status register. + cx_write(sram_ch->int_stat, _intr_msk); + + //Set the interrupt mask register, enable irq. + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << sram_ch->irq_bit)); + tmp = cx_read(sram_ch->int_msk); + cx_write(sram_ch->int_msk, tmp |= _intr_msk); + + err = + request_irq(dev->pci->irq, cx25821_upstream_irq, + IRQF_SHARED | IRQF_DISABLED, dev->name, dev); + if (err < 0) { + printk(KERN_ERR "%s: can't get upstream IRQ %d\n", dev->name, + dev->pci->irq); + goto fail_irq; + } + + // Start the DMA engine + tmp = cx_read(sram_ch->dma_ctl); + cx_set(sram_ch->dma_ctl, tmp | FLD_VID_RISC_EN); + + dev->_is_running = 1; + dev->_is_first_frame = 1; + + return 0; + + fail_irq: + cx25821_dev_unregister(dev); + return err; +} + +int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, int channel_select, + int pixel_format) +{ + struct sram_channel *sram_ch; + u32 tmp; + int retval = 0; + int err = 0; + int data_frame_size = 0; + int risc_buffer_size = 0; + int str_length = 0; + + if (dev->_is_running) { + printk("Video Channel is still running so return!\n"); + return 0; + } + + dev->_channel_upstream_select = channel_select; + sram_ch = &dev->sram_channels[channel_select]; + + INIT_WORK(&dev->_irq_work_entry, cx25821_vidups_handler); + dev->_irq_queues = create_singlethread_workqueue("cx25821_workqueue"); + + if (!dev->_irq_queues) { + printk + ("cx25821: create_singlethread_workqueue() for Video FAILED!\n"); + return -ENOMEM; + } + // 656/VIP SRC Upstream Channel I & J and 7 - Host Bus Interface for channel A-C + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp | 0x1B0001FF); + + dev->_is_running = 0; + dev->_frame_count = 0; + dev->_file_status = RESET_STATUS; + dev->_lines_count = dev->_isNTSC ? 480 : 576; + dev->_pixel_format = pixel_format; + dev->_line_size = + (dev->_pixel_format == + PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; + data_frame_size = dev->_isNTSC ? NTSC_DATA_BUF_SZ : PAL_DATA_BUF_SZ; + risc_buffer_size = + dev->_isNTSC ? NTSC_RISC_BUF_SIZE : PAL_RISC_BUF_SIZE; + + if (dev->input_filename) { + str_length = strlen(dev->input_filename); + dev->_filename = (char *)kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename) + goto error; + + memcpy(dev->_filename, dev->input_filename, str_length + 1); + } else { + str_length = strlen(dev->_defaultname); + dev->_filename = (char *)kmalloc(str_length + 1, GFP_KERNEL); + + if (!dev->_filename) + goto error; + + memcpy(dev->_filename, dev->_defaultname, str_length + 1); + } + + //Default if filename is empty string + if (strcmp(dev->input_filename, "") == 0) { + if (dev->_isNTSC) { + dev->_filename = + (dev->_pixel_format == + PIXEL_FRMT_411) ? "/root/vid411.yuv" : + "/root/vidtest.yuv"; + } else { + dev->_filename = + (dev->_pixel_format == + PIXEL_FRMT_411) ? "/root/pal411.yuv" : + "/root/pal422.yuv"; + } + } + + dev->_is_running = 0; + dev->_frame_count = 0; + dev->_file_status = RESET_STATUS; + dev->_lines_count = dev->_isNTSC ? 480 : 576; + dev->_pixel_format = pixel_format; + dev->_line_size = + (dev->_pixel_format == + PIXEL_FRMT_422) ? (WIDTH_D1 * 2) : (WIDTH_D1 * 3) / 2; + + retval = + cx25821_sram_channel_setup_upstream(dev, sram_ch, dev->_line_size, + 0); + + /* setup fifo + format */ + cx25821_set_pixelengine(dev, sram_ch, dev->_pixel_format); + + dev->upstream_riscbuf_size = risc_buffer_size * 2; + dev->upstream_databuf_size = data_frame_size * 2; + + //Allocating buffers and prepare RISC program + retval = cx25821_upstream_buffer_prepare(dev, sram_ch, dev->_line_size); + if (retval < 0) { + printk(KERN_ERR + "%s: Failed to set up Video upstream buffers!\n", + dev->name); + goto error; + } + + cx25821_start_video_dma_upstream(dev, sram_ch); + + return 0; + + error: + cx25821_dev_unregister(dev); + + return err; +} diff --git a/linux/drivers/staging/cx25821/cx25821-video-upstream.h b/linux/drivers/staging/cx25821/cx25821-video-upstream.h new file mode 100644 index 000000000..eb8c59796 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video-upstream.h @@ -0,0 +1,110 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <hiep.huynh@conexant.com>, <shu.lin@conexant.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "compat.h" +#include <linux/mutex.h> +#include <linux/workqueue.h> + +#define OUTPUT_FRMT_656 0 +#define OPEN_FILE_1 0 +#define NUM_PROGS 8 +#define NUM_FRAMES 2 +#define ODD_FIELD 0 +#define EVEN_FIELD 1 +#define TOP_OFFSET 0 +#define FIFO_DISABLE 0 +#define FIFO_ENABLE 1 +#define TEST_FRAMES 5 +#define END_OF_FILE 0 +#define IN_PROGRESS 1 +#define RESET_STATUS -1 +#define NUM_NO_OPS 5 + +// PAL and NTSC line sizes and number of lines. +#define WIDTH_D1 720 +#define NTSC_LINES_PER_FRAME 480 +#define PAL_LINES_PER_FRAME 576 +#define PAL_LINE_SZ 1440 +#define Y422_LINE_SZ 1440 +#define Y411_LINE_SZ 1080 +#define NTSC_FIELD_HEIGHT 240 +#define NTSC_ODD_FLD_LINES 241 +#define PAL_FIELD_HEIGHT 288 + +#define FRAME_SIZE_NTSC_Y422 (NTSC_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_NTSC_Y411 (NTSC_LINES_PER_FRAME * Y411_LINE_SZ) +#define FRAME_SIZE_PAL_Y422 (PAL_LINES_PER_FRAME * Y422_LINE_SZ) +#define FRAME_SIZE_PAL_Y411 (PAL_LINES_PER_FRAME * Y411_LINE_SZ) + +#define NTSC_DATA_BUF_SZ (Y422_LINE_SZ * NTSC_LINES_PER_FRAME) +#define PAL_DATA_BUF_SZ (Y422_LINE_SZ * PAL_LINES_PER_FRAME) + +#define RISC_WRITECR_INSTRUCTION_SIZE 16 +#define RISC_SYNC_INSTRUCTION_SIZE 4 +#define JUMP_INSTRUCTION_SIZE 12 +#define MAXSIZE_NO_OPS 36 +#define DWORD_SIZE 4 + +#define USE_RISC_NOOP_VIDEO 1 + +#ifdef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) + +#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) + +#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE) + +#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) + +#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) + +#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + JUMP_INSTRUCTION_SIZE + NUM_NO_OPS*DWORD_SIZE) + +#define NTSC_RISC_BUF_SIZE (2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE)) + +#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE + 2*NUM_NO_OPS*DWORD_SIZE) + +#endif + +#ifndef USE_RISC_NOOP_VIDEO +#define PAL_US_VID_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + \ + RISC_SYNC_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) + +#define PAL_RISC_BUF_SIZE (2 * PAL_US_VID_PROG_SIZE) + +#define PAL_VID_PROG_SIZE ((PAL_FIELD_HEIGHT*2) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE ) + +#define ODD_FLD_PAL_PROG_SIZE ((PAL_FIELD_HEIGHT) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE ) +#define ODD_FLD_NTSC_PROG_SIZE ((NTSC_ODD_FLD_LINES) * 3 * DWORD_SIZE + RISC_SYNC_INSTRUCTION_SIZE + RISC_WRITECR_INSTRUCTION_SIZE ) + +#define NTSC_US_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + 1) * 3 * DWORD_SIZE + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE) +#define NTSC_RISC_BUF_SIZE ( 2 * (RISC_SYNC_INSTRUCTION_SIZE + NTSC_US_VID_PROG_SIZE) ) +#define FRAME1_VID_PROG_SIZE ((NTSC_ODD_FLD_LINES + NTSC_FIELD_HEIGHT) * 3 * DWORD_SIZE + 2*RISC_SYNC_INSTRUCTION_SIZE + \ + RISC_WRITECR_INSTRUCTION_SIZE + JUMP_INSTRUCTION_SIZE ) +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-video.c b/linux/drivers/staging/cx25821/cx25821-video.c new file mode 100644 index 000000000..8834bc80a --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video.c @@ -0,0 +1,1299 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +MODULE_DESCRIPTION("v4l2 driver module for cx25821 based TV cards"); +MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>"); +MODULE_LICENSE("GPL"); + +static unsigned int video_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; +static unsigned int radio_nr[] = {[0 ... (CX25821_MAXBOARDS - 1)] = UNSET }; + +module_param_array(video_nr, int, NULL, 0444); +module_param_array(radio_nr, int, NULL, 0444); + +MODULE_PARM_DESC(video_nr, "video device numbers"); +MODULE_PARM_DESC(radio_nr, "radio device numbers"); + +static unsigned int video_debug = VIDEO_DEBUG; +module_param(video_debug, int, 0644); +MODULE_PARM_DESC(video_debug, "enable debug messages [video]"); + +static unsigned int irq_debug; +module_param(irq_debug, int, 0644); +MODULE_PARM_DESC(irq_debug, "enable debug messages [IRQ handler]"); + +unsigned int vid_limit = 16; +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes"); + +static void init_controls(struct cx25821_dev *dev, int chan_num); + +#define FORMAT_FLAGS_PACKED 0x01 + +struct cx25821_fmt formats[] = { + { + .name = "8 bpp, gray", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:1:1, packed, Y41P", + .fourcc = V4L2_PIX_FMT_Y41P, + .depth = 12, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + .flags = FORMAT_FLAGS_PACKED, + }, { + .name = "4:2:0, YUV", + .fourcc = V4L2_PIX_FMT_YUV420, + .depth = 12, + .flags = FORMAT_FLAGS_PACKED, + }, +}; + +int get_format_size(void) +{ + return ARRAY_SIZE(formats); +} + +struct cx25821_fmt *format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + if (fourcc == V4L2_PIX_FMT_Y41P || fourcc == V4L2_PIX_FMT_YUV411P) { + return formats + 1; + } + + for (i = 0; i < ARRAY_SIZE(formats); i++) + if (formats[i].fourcc == fourcc) + return formats + i; + + printk(KERN_ERR "%s(0x%08x) NOT FOUND\n", __func__, fourcc); + return NULL; +} + +void dump_video_queue(struct cx25821_dev *dev, struct cx25821_dmaqueue *q) +{ + struct cx25821_buffer *buf; + struct list_head *item; + dprintk(1, "%s()\n", __func__); + + if (!list_empty(&q->active)) { + list_for_each(item, &q->active) + buf = list_entry(item, struct cx25821_buffer, vb.queue); + } + + if (!list_empty(&q->queued)) { + list_for_each(item, &q->queued) + buf = list_entry(item, struct cx25821_buffer, vb.queue); + } + +} + +void cx25821_video_wakeup(struct cx25821_dev *dev, struct cx25821_dmaqueue *q, + u32 count) +{ + struct cx25821_buffer *buf; + int bc; + + for (bc = 0;; bc++) { + if (list_empty(&q->active)) { + dprintk(1, "bc=%d (=0: active empty)\n", bc); + break; + } + + buf = + list_entry(q->active.next, struct cx25821_buffer, vb.queue); + + /* count comes from the hw and it is 16bit wide -- + * this trick handles wrap-arounds correctly for + * up to 32767 buffers in flight... */ + if ((s16) (count - buf->count) < 0) { + break; + } + + do_gettimeofday(&buf->vb.ts); + buf->vb.state = VIDEOBUF_DONE; + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); + } + + if (list_empty(&q->active)) + del_timer(&q->timeout); + else + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + if (bc != 1) + printk(KERN_ERR "%s: %d buffers handled (should be 1)\n", + __func__, bc); +} + +#ifdef TUNER_FLAG +int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm) +{ + dprintk(1, "%s(norm = 0x%08x) name: [%s]\n", __func__, + (unsigned int)norm, v4l2_norm_to_name(norm)); + + dev->tvnorm = norm; + + /* Tell the internal A/V decoder */ + cx25821_call_all(dev, core, s_std, norm); + + return 0; +} +#endif + +struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, + struct pci_dev *pci, + struct video_device *template, + char *type) +{ + struct video_device *vfd; + dprintk(1, "%s()\n", __func__); + + vfd = video_device_alloc(); + if (NULL == vfd) + return NULL; + *vfd = *template; + vfd->minor = -1; + vfd->v4l2_dev = &dev->v4l2_dev; + vfd->release = video_device_release; + snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)", dev->name, type, + cx25821_boards[dev->board].name); + return vfd; +} + +/* +static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) +{ + int i; + + if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) + return -EINVAL; + for (i = 0; i < CX25821_CTLS; i++) + if (cx25821_ctls[i].v.id == qctrl->id) + break; + if (i == CX25821_CTLS) { + *qctrl = no_ctl; + return 0; + } + *qctrl = cx25821_ctls[i].v; + return 0; +} +*/ + +// resource management +int res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bit) +{ + dprintk(1, "%s()\n", __func__); + if (fh->resources & bit) + /* have it already allocated */ + return 1; + + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources & bit) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + fh->resources |= bit; + dev->resources |= bit; + dprintk(1, "res: get %d\n", bit); + mutex_unlock(&dev->lock); + return 1; +} + +int res_check(struct cx25821_fh *fh, unsigned int bit) +{ + return fh->resources & bit; +} + +int res_locked(struct cx25821_dev *dev, unsigned int bit) +{ + return dev->resources & bit; +} + +void res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, unsigned int bits) +{ + BUG_ON((fh->resources & bits) != bits); + dprintk(1, "%s()\n", __func__); + + mutex_lock(&dev->lock); + fh->resources &= ~bits; + dev->resources &= ~bits; + dprintk(1, "res: put %d\n", bits); + mutex_unlock(&dev->lock); +} + +int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input) +{ + struct v4l2_routing route; + memset(&route, 0, sizeof(route)); + + dprintk(1, "%s() video_mux: %d [vmux=%d, gpio=0x%x,0x%x,0x%x,0x%x]\n", + __func__, input, INPUT(input)->vmux, INPUT(input)->gpio0, + INPUT(input)->gpio1, INPUT(input)->gpio2, INPUT(input)->gpio3); + dev->input = input; + + route.input = INPUT(input)->vmux; + + /* Tell the internal A/V decoder */ + cx25821_call_all(dev, video, s_routing, INPUT(input)->vmux, 0, 0); + + return 0; +} + +int cx25821_start_video_dma(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, + struct cx25821_buffer *buf, + struct sram_channel *channel) +{ + int tmp = 0; + + /* setup fifo + format */ + cx25821_sram_channel_setup(dev, channel, buf->bpl, buf->risc.dma); + + /* reset counter */ + cx_write(channel->gpcnt_ctl, 3); + q->count = 1; + + /* enable irq */ + cx_set(PCI_INT_MSK, cx_read(PCI_INT_MSK) | (1 << channel->i)); + cx_set(channel->int_msk, 0x11); + + /* start dma */ + cx_write(channel->dma_ctl, 0x11); /* FIFO and RISC enable */ + + /* make sure upstream setting if any is reversed */ + tmp = cx_read(VID_CH_MODE_SEL); + cx_write(VID_CH_MODE_SEL, tmp & 0xFFFFFE00); + + return 0; +} + +int cx25821_restart_video_queue(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, + struct sram_channel *channel) +{ + struct cx25821_buffer *buf, *prev; + struct list_head *item; + + if (!list_empty(&q->active)) { + buf = + list_entry(q->active.next, struct cx25821_buffer, vb.queue); + + cx25821_start_video_dma(dev, q, buf, channel); + + list_for_each(item, &q->active) { + buf = list_entry(item, struct cx25821_buffer, vb.queue); + buf->count = q->count++; + } + + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + return 0; + } + + prev = NULL; + for (;;) { + if (list_empty(&q->queued)) + return 0; + + buf = + list_entry(q->queued.next, struct cx25821_buffer, vb.queue); + + if (NULL == prev) { + list_move_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, channel); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + } else if (prev->vb.width == buf->vb.width && + prev->vb.height == buf->vb.height && + prev->fmt == buf->fmt) { + list_move_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + prev->risc.jmp[2] = cpu_to_le32(0); /* Bits 63 - 32 */ + } else { + return 0; + } + prev = buf; + } +} + +void cx25821_vid_timeout(unsigned long data) +{ + struct cx25821_data *timeout_data = (struct cx25821_data *)data; + struct cx25821_dev *dev = timeout_data->dev; + struct sram_channel *channel = timeout_data->channel; + struct cx25821_dmaqueue *q = &dev->vidq[channel->i]; + struct cx25821_buffer *buf; + unsigned long flags; + + //cx25821_sram_channel_dump(dev, channel); + cx_clear(channel->dma_ctl, 0x11); + + spin_lock_irqsave(&dev->slock, flags); + while (!list_empty(&q->active)) { + buf = + list_entry(q->active.next, struct cx25821_buffer, vb.queue); + list_del(&buf->vb.queue); + + buf->vb.state = VIDEOBUF_ERROR; + wake_up(&buf->vb.done); + } + + cx25821_restart_video_queue(dev, q, channel); + spin_unlock_irqrestore(&dev->slock, flags); +} + +int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status) +{ + u32 count = 0; + int handled = 0; + u32 mask; + struct sram_channel *channel = &dev->sram_channels[chan_num]; + + mask = cx_read(channel->int_msk); + if (0 == (status & mask)) + return handled; + + cx_write(channel->int_stat, status); + + /* risc op code error */ + if (status & (1 << 16)) { + printk(KERN_WARNING "%s, %s: video risc op code error\n", + dev->name, channel->name); + cx_clear(channel->dma_ctl, 0x11); + cx25821_sram_channel_dump(dev, channel); + } + + /* risc1 y */ + if (status & FLD_VID_DST_RISC1) { + spin_lock(&dev->slock); + count = cx_read(channel->gpcnt); + cx25821_video_wakeup(dev, &dev->vidq[channel->i], count); + spin_unlock(&dev->slock); + handled++; + } + + /* risc2 y */ + if (status & 0x10) { + dprintk(2, "stopper video\n"); + spin_lock(&dev->slock); + cx25821_restart_video_queue(dev, &dev->vidq[channel->i], + channel); + spin_unlock(&dev->slock); + handled++; + } + return handled; +} + +void cx25821_videoioctl_unregister(struct cx25821_dev *dev) +{ + if (dev->ioctl_dev) { + if (dev->ioctl_dev->minor != -1) + video_unregister_device(dev->ioctl_dev); + else + video_device_release(dev->ioctl_dev); + + dev->ioctl_dev = NULL; + } +} + +void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num) +{ + cx_clear(PCI_INT_MSK, 1); + + if (dev->video_dev[chan_num]) { + if (-1 != dev->video_dev[chan_num]->minor) + video_unregister_device(dev->video_dev[chan_num]); + else + video_device_release(dev->video_dev[chan_num]); + + dev->video_dev[chan_num] = NULL; + + btcx_riscmem_free(dev->pci, &dev->vidq[chan_num].stopper); + + printk(KERN_WARNING "device %d released!\n", chan_num); + } + +} + +int cx25821_video_register(struct cx25821_dev *dev, int chan_num, + struct video_device *video_template) +{ + int err; + + spin_lock_init(&dev->slock); + + //printk(KERN_WARNING "Channel %d\n", chan_num); + +#ifdef TUNER_FLAG + dev->tvnorm = video_template->current_norm; +#endif + + /* init video dma queues */ + dev->timeout_data[chan_num].dev = dev; + dev->timeout_data[chan_num].channel = &dev->sram_channels[chan_num]; + INIT_LIST_HEAD(&dev->vidq[chan_num].active); + INIT_LIST_HEAD(&dev->vidq[chan_num].queued); + dev->vidq[chan_num].timeout.function = cx25821_vid_timeout; + dev->vidq[chan_num].timeout.data = + (unsigned long)&dev->timeout_data[chan_num]; + init_timer(&dev->vidq[chan_num].timeout); + cx25821_risc_stopper(dev->pci, &dev->vidq[chan_num].stopper, + dev->sram_channels[chan_num].dma_ctl, 0x11, 0); + + /* register v4l devices */ + dev->video_dev[chan_num] = + cx25821_vdev_init(dev, dev->pci, video_template, "video"); + err = + video_register_device(dev->video_dev[chan_num], VFL_TYPE_GRABBER, + video_nr[dev->nr]); + + if (err < 0) { + goto fail_unreg; + } + //set PCI interrupt + cx_set(PCI_INT_MSK, 0xff); + + /* initial device configuration */ + mutex_lock(&dev->lock); +#ifdef TUNER_FLAG + cx25821_set_tvnorm(dev, dev->tvnorm); +#endif + mutex_unlock(&dev->lock); + + init_controls(dev, chan_num); + + return 0; + + fail_unreg: + cx25821_video_unregister(dev, chan_num); + return err; +} + +int buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size) +{ + struct cx25821_fh *fh = q->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + + if (0 == *count) + *count = 32; + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct cx25821_fh *fh = q->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + int rc, init_buffer = 0; + u32 line0_offset, line1_offset; + struct videobuf_dmabuf *dma = videobuf_to_dma(&buf->vb); + int bpl_local = LINE_SIZE_D1; + int channel_opened = 0; + + BUG_ON(NULL == fh->fmt); + if (fh->width < 48 || fh->width > 720 || + fh->height < 32 || fh->height > 576) + return -EINVAL; + + buf->vb.size = (fh->width * fh->height * fh->fmt->depth) >> 3; + + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + init_buffer = 1; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + init_buffer = 1; + rc = videobuf_iolock(q, &buf->vb, NULL); + if (0 != rc) { + printk(KERN_DEBUG "videobuf_iolock failed!\n"); + goto fail; + } + } + + dprintk(1, "init_buffer=%d\n", init_buffer); + + if (init_buffer) { + + channel_opened = dev->channel_opened; + channel_opened = (channel_opened < 0 + || channel_opened > 7) ? 7 : channel_opened; + + if (dev->pixel_formats[channel_opened] == PIXEL_FRMT_411) + buf->bpl = (buf->fmt->depth * buf->vb.width) >> 3; + else + buf->bpl = (buf->fmt->depth >> 3) * (buf->vb.width); + + if (dev->pixel_formats[channel_opened] == PIXEL_FRMT_411) { + bpl_local = buf->bpl; + } else { + bpl_local = buf->bpl; //Default + + if (channel_opened >= 0 && channel_opened <= 7) { + if (dev->use_cif_resolution[channel_opened]) { + if (dev->tvnorm & V4L2_STD_PAL_BG + || dev->tvnorm & V4L2_STD_PAL_DK) + bpl_local = 352 << 1; + else + bpl_local = + dev-> + cif_width[channel_opened] << + 1; + } + } + } + + switch (buf->vb.field) { + case V4L2_FIELD_TOP: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, 0, UNSET, + buf->bpl, 0, buf->vb.height); + break; + case V4L2_FIELD_BOTTOM: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, UNSET, 0, + buf->bpl, 0, buf->vb.height); + break; + case V4L2_FIELD_INTERLACED: + /* All other formats are top field first */ + line0_offset = 0; + line1_offset = buf->bpl; + dprintk(1, "top field first\n"); + + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, line0_offset, + bpl_local, bpl_local, bpl_local, + buf->vb.height >> 1); + break; + case V4L2_FIELD_SEQ_TB: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, + 0, buf->bpl * (buf->vb.height >> 1), + buf->bpl, 0, buf->vb.height >> 1); + break; + case V4L2_FIELD_SEQ_BT: + cx25821_risc_buffer(dev->pci, &buf->risc, + dma->sglist, + buf->bpl * (buf->vb.height >> 1), 0, + buf->bpl, 0, buf->vb.height >> 1); + break; + default: + BUG(); + } + } + + dprintk(2, "[%p/%d] buffer_prep - %dx%d %dbpp \"%s\" - dma=0x%08lx\n", + buf, buf->vb.i, fh->width, fh->height, fh->fmt->depth, + fh->fmt->name, (unsigned long)buf->risc.dma); + + buf->vb.state = VIDEOBUF_PREPARED; + + return 0; + + fail: + cx25821_free_buffer(q, buf); + return rc; +} + +void buffer_release(struct videobuf_queue *q, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + + cx25821_free_buffer(q, buf); +} + +struct videobuf_queue *get_queue(struct cx25821_fh *fh) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return &fh->vidq; + default: + BUG(); + return NULL; + } +} + +int get_resource(struct cx25821_fh *fh, int resource) +{ + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + return resource; + default: + BUG(); + return 0; + } +} + +int video_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct cx25821_fh *fh = file->private_data; + + return videobuf_mmap_mapper(get_queue(fh), vma); +} + +/* VIDEO IOCTLS */ +int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *f) +{ + struct cx25821_fmt *fmt; + enum v4l2_field field; + unsigned int maxw, maxh; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) + return -EINVAL; + + field = f->fmt.pix.field; + maxw = 720; + maxh = 576; + + if (V4L2_FIELD_ANY == field) { + field = (f->fmt.pix.height > maxh / 2) + ? V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP; + } + + switch (field) { + case V4L2_FIELD_TOP: + case V4L2_FIELD_BOTTOM: + maxh = maxh / 2; + break; + case V4L2_FIELD_INTERLACED: + break; + default: + return -EINVAL; + } + + f->fmt.pix.field = field; + if (f->fmt.pix.height < 32) + f->fmt.pix.height = 32; + if (f->fmt.pix.height > maxh) + f->fmt.pix.height = maxh; + if (f->fmt.pix.width < 48) + f->fmt.pix.width = 48; + if (f->fmt.pix.width > maxw) + f->fmt.pix.width = maxw; + f->fmt.pix.width &= ~0x03; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + strcpy(cap->driver, "cx25821"); + strlcpy(cap->card, cx25821_boards[dev->board].name, sizeof(cap->card)); + sprintf(cap->bus_info, "PCIe:%s", pci_name(dev->pci)); + cap->version = CX25821_VERSION_CODE; + cap->capabilities = + V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + if (UNSET != dev->tuner_type) + cap->capabilities |= V4L2_CAP_TUNER; + return 0; +} + +int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(formats))) + return -EINVAL; + + strlcpy(f->description, formats[f->index].name, sizeof(f->description)); + f->pixelformat = formats[f->index].fourcc; + + return 0; +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct cx25821_fh *fh = priv; + struct videobuf_queue *q; + struct v4l2_requestbuffers req; + unsigned int i; + int err; + + q = get_queue(fh); + memset(&req, 0, sizeof(req)); + req.type = q->type; + req.count = 8; + req.memory = V4L2_MEMORY_MMAP; + err = videobuf_reqbufs(q, &req); + if (err < 0) + return err; + + mbuf->frames = req.count; + mbuf->size = 0; + for (i = 0; i < mbuf->frames; i++) { + mbuf->offsets[i] = q->bufs[i]->boff; + mbuf->size += q->bufs[i]->bsize; + } + return 0; +} +#endif + +int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_reqbufs(get_queue(fh), p); +} + +int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_querybuf(get_queue(fh), p); +} + +int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_qbuf(get_queue(fh), p); +} + +int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; + + *p = v4l2_prio_max(&dev->prio); + + return 0; +} + +int vidioc_s_priority(struct file *file, void *f, enum v4l2_priority prio) +{ + struct cx25821_fh *fh = f; + struct cx25821_dev *dev = ((struct cx25821_fh *)f)->dev; + + return v4l2_prio_change(&dev->prio, &fh->prio, prio); +} + +#ifdef TUNER_FLAG +int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + dprintk(1, "%s()\n", __func__); + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + if (dev->tvnorm == *tvnorms) { + return 0; + } + + mutex_lock(&dev->lock); + cx25821_set_tvnorm(dev, *tvnorms); + mutex_unlock(&dev->lock); + + medusa_set_videostandard(dev); + + return 0; +} +#endif + +int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i) +{ + static const char *iname[] = { + [CX25821_VMUX_COMPOSITE] = "Composite", + [CX25821_VMUX_SVIDEO] = "S-Video", + [CX25821_VMUX_DEBUG] = "for debug only", + }; + unsigned int n; + dprintk(1, "%s()\n", __func__); + + n = i->index; + if (n > 2) + return -EINVAL; + + if (0 == INPUT(n)->type) + return -EINVAL; + + memset(i, 0, sizeof(*i)); + i->index = n; + i->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(i->name, iname[INPUT(n)->type]); + + i->std = CX25821_NORMS; + return 0; +} + +int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *i) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + dprintk(1, "%s()\n", __func__); + return cx25821_enum_input(dev, i); +} + +int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + *i = dev->input; + dprintk(1, "%s() returns %d\n", __func__, *i); + return 0; +} + +int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + dprintk(1, "%s(%d)\n", __func__, i); + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + if (i > 2) { + dprintk(1, "%s() -EINVAL\n", __func__); + return -EINVAL; + } + + mutex_lock(&dev->lock); + cx25821_video_mux(dev, i); + mutex_unlock(&dev->lock); + return 0; +} + +#ifdef TUNER_FLAG +int vidioc_g_frequency(struct file *file, void *priv, struct v4l2_frequency *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + f->frequency = dev->freq; + + cx25821_call_all(dev, tuner, g_frequency, f); + + return 0; +} + +int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f) +{ + mutex_lock(&dev->lock); + dev->freq = f->frequency; + + cx25821_call_all(dev, tuner, s_frequency, f); + + /* When changing channels it is required to reset TVAUDIO */ + msleep(10); + + mutex_unlock(&dev->lock); + + return 0; +} + +int vidioc_s_frequency(struct file *file, void *priv, struct v4l2_frequency *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_freq(dev, f); +} +#endif + +#ifdef CONFIG_VIDEO_ADV_DEBUG +int vidioc_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + + cx25821_call_all(dev, core, g_register, reg); + + return 0; +} + +int vidioc_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)fh)->dev; + + if (!v4l2_chip_match_host(®->match)) + return -EINVAL; + + cx25821_call_all(dev, core, s_register, reg); + + return 0; +} + +#endif + +#ifdef TUNER_FLAG +int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + + t->signal = 0xffff; /* LOCKED */ + return 0; +} + +int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(1, "%s()\n", __func__); + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + return 0; +} + +#endif +// ****************************************************************************************** +static const struct v4l2_queryctrl no_ctl = { + .name = "42", + .flags = V4L2_CTRL_FLAG_DISABLED, +}; + +static struct v4l2_queryctrl cx25821_ctls[] = { + /* --- video --- */ + { + .id = V4L2_CID_BRIGHTNESS, + .name = "Brightness", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 6200, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_CONTRAST, + .name = "Contrast", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 5000, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_SATURATION, + .name = "Saturation", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 5000, + .type = V4L2_CTRL_TYPE_INTEGER, + }, { + .id = V4L2_CID_HUE, + .name = "Hue", + .minimum = 0, + .maximum = 10000, + .step = 1, + .default_value = 5000, + .type = V4L2_CTRL_TYPE_INTEGER, + } +}; +static const int CX25821_CTLS = ARRAY_SIZE(cx25821_ctls); + +static int cx25821_ctrl_query(struct v4l2_queryctrl *qctrl) +{ + int i; + + if (qctrl->id < V4L2_CID_BASE || qctrl->id >= V4L2_CID_LASTP1) + return -EINVAL; + for (i = 0; i < CX25821_CTLS; i++) + if (cx25821_ctls[i].id == qctrl->id) + break; + if (i == CX25821_CTLS) { + *qctrl = no_ctl; + return 0; + } + *qctrl = cx25821_ctls[i]; + return 0; +} + +int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl) +{ + return cx25821_ctrl_query(qctrl); +} + +/* ------------------------------------------------------------------ */ +/* VIDEO CTRL IOCTLS */ + +static const struct v4l2_queryctrl *ctrl_by_id(unsigned int id) +{ + unsigned int i; + + for (i = 0; i < CX25821_CTLS; i++) + if (cx25821_ctls[i].id == id) + return cx25821_ctls + i; + return NULL; +} + +int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctl) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + const struct v4l2_queryctrl *ctrl; + + ctrl = ctrl_by_id(ctl->id); + + if (NULL == ctrl) + return -EINVAL; + switch (ctl->id) { + case V4L2_CID_BRIGHTNESS: + ctl->value = dev->ctl_bright; + break; + case V4L2_CID_HUE: + ctl->value = dev->ctl_hue; + break; + case V4L2_CID_CONTRAST: + ctl->value = dev->ctl_contrast; + break; + case V4L2_CID_SATURATION: + ctl->value = dev->ctl_saturation; + break; + } + return 0; +} + +int cx25821_set_control(struct cx25821_dev *dev, + struct v4l2_control *ctl, int chan_num) +{ + int err; + const struct v4l2_queryctrl *ctrl; + + err = -EINVAL; + + ctrl = ctrl_by_id(ctl->id); + + if (NULL == ctrl) + return err; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER: + if (ctl->value < ctrl->minimum) + ctl->value = ctrl->minimum; + if (ctl->value > ctrl->maximum) + ctl->value = ctrl->maximum; + break; + default: + /* nothing */ ; + }; + + switch (ctl->id) { + case V4L2_CID_BRIGHTNESS: + dev->ctl_bright = ctl->value; + medusa_set_brightness(dev, ctl->value, chan_num); + break; + case V4L2_CID_HUE: + dev->ctl_hue = ctl->value; + medusa_set_hue(dev, ctl->value, chan_num); + break; + case V4L2_CID_CONTRAST: + dev->ctl_contrast = ctl->value; + medusa_set_contrast(dev, ctl->value, chan_num); + break; + case V4L2_CID_SATURATION: + dev->ctl_saturation = ctl->value; + medusa_set_saturation(dev, ctl->value, chan_num); + break; + } + + err = 0; + + return err; +} + +static void init_controls(struct cx25821_dev *dev, int chan_num) +{ + struct v4l2_control ctrl; + int i; + for (i = 0; i < CX25821_CTLS; i++) { + ctrl.id = cx25821_ctls[i].id; + ctrl.value = cx25821_ctls[i].default_value; + + cx25821_set_control(dev, &ctrl, chan_num); + } +} + +int vidioc_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cropcap) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + cropcap->bounds.top = cropcap->bounds.left = 0; + cropcap->bounds.width = 720; + cropcap->bounds.height = dev->tvnorm == V4L2_STD_PAL_BG ? 576 : 480; + cropcap->pixelaspect.numerator = + dev->tvnorm == V4L2_STD_PAL_BG ? 59 : 10; + cropcap->pixelaspect.denominator = + dev->tvnorm == V4L2_STD_PAL_BG ? 54 : 11; + cropcap->defrect = cropcap->bounds; + return 0; +} + +int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + // vidioc_s_crop not supported + return -EINVAL; +} + +int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop) +{ + // vidioc_g_crop not supported + return -EINVAL; +} + +int vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm) +{ + // medusa does not support video standard sensing of current input + *norm = CX25821_NORMS; + + return 0; +} + +int is_valid_width(u32 width, v4l2_std_id tvnorm) +{ + if (tvnorm == V4L2_STD_PAL_BG) { + if (width == 352 || width == 720) + return 1; + else + return 0; + } + + if (tvnorm == V4L2_STD_NTSC_M) { + if (width == 320 || width == 352 || width == 720) + return 1; + else + return 0; + } + return 0; +} + +int is_valid_height(u32 height, v4l2_std_id tvnorm) +{ + if (tvnorm == V4L2_STD_PAL_BG) { + if (height == 576 || height == 288) + return 1; + else + return 0; + } + + if (tvnorm == V4L2_STD_NTSC_M) { + if (height == 480 || height == 240) + return 1; + else + return 0; + } + + return 0; +} diff --git a/linux/drivers/staging/cx25821/cx25821-video.h b/linux/drivers/staging/cx25821/cx25821-video.h new file mode 100644 index 000000000..68df23bab --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video.h @@ -0,0 +1,195 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef CX25821_VIDEO_H_ +#define CX25821_VIDEO_H_ + +#include <linux/init.h> +#include <linux/list.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kmod.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include "compat.h" +#include <linux/kthread.h> +#include <asm/div64.h> + +#include "cx25821.h" +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +/* Include V4L1 specific functions. Should be removed soon */ +#include <linux/videodev.h> +#endif + +#define TUNER_FLAG + +#define VIDEO_DEBUG 0 + +#define dprintk(level, fmt, arg...)\ + do { if (VIDEO_DEBUG >= level)\ + printk(KERN_DEBUG "%s/0: " fmt, dev->name, ## arg);\ + } while (0) + +//For IOCTL to identify running upstream +#define UPSTREAM_START_VIDEO 700 +#define UPSTREAM_STOP_VIDEO 701 +#define UPSTREAM_START_AUDIO 702 +#define UPSTREAM_STOP_AUDIO 703 +#define UPSTREAM_DUMP_REGISTERS 702 +#define SET_VIDEO_STD 800 +#define SET_PIXEL_FORMAT 1000 +#define ENABLE_CIF_RESOLUTION 1001 + +#define REG_READ 900 +#define REG_WRITE 901 +#define MEDUSA_READ 910 +#define MEDUSA_WRITE 911 + +extern struct sram_channel *channel0; +extern struct sram_channel *channel1; +extern struct sram_channel *channel2; +extern struct sram_channel *channel3; +extern struct sram_channel *channel4; +extern struct sram_channel *channel5; +extern struct sram_channel *channel6; +extern struct sram_channel *channel7; +extern struct sram_channel *channel9; +extern struct sram_channel *channel10; +extern struct sram_channel *channel11; +extern struct video_device cx25821_video_template0; +extern struct video_device cx25821_video_template1; +extern struct video_device cx25821_video_template2; +extern struct video_device cx25821_video_template3; +extern struct video_device cx25821_video_template4; +extern struct video_device cx25821_video_template5; +extern struct video_device cx25821_video_template6; +extern struct video_device cx25821_video_template7; +extern struct video_device cx25821_video_template9; +extern struct video_device cx25821_video_template10; +extern struct video_device cx25821_video_template11; +extern struct video_device cx25821_videoioctl_template; +//extern const u32 *ctrl_classes[]; + +extern unsigned int vid_limit; + +#define FORMAT_FLAGS_PACKED 0x01 +extern struct cx25821_fmt formats[]; +extern struct cx25821_fmt *format_by_fourcc(unsigned int fourcc); +extern struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; + +extern void dump_video_queue(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q); +extern void cx25821_video_wakeup(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, u32 count); + +#ifdef TUNER_FLAG +extern int cx25821_set_tvnorm(struct cx25821_dev *dev, v4l2_std_id norm); +#endif + +extern int res_get(struct cx25821_dev *dev, struct cx25821_fh *fh, + unsigned int bit); +extern int res_check(struct cx25821_fh *fh, unsigned int bit); +extern int res_locked(struct cx25821_dev *dev, unsigned int bit); +extern void res_free(struct cx25821_dev *dev, struct cx25821_fh *fh, + unsigned int bits); +extern int cx25821_video_mux(struct cx25821_dev *dev, unsigned int input); +extern int cx25821_start_video_dma(struct cx25821_dev *dev, + struct cx25821_dmaqueue *q, + struct cx25821_buffer *buf, + struct sram_channel *channel); + +extern int cx25821_set_scale(struct cx25821_dev *dev, unsigned int width, + unsigned int height, enum v4l2_field field); +extern int cx25821_video_irq(struct cx25821_dev *dev, int chan_num, u32 status); +extern void cx25821_video_unregister(struct cx25821_dev *dev, int chan_num); +extern int cx25821_video_register(struct cx25821_dev *dev, int chan_num, + struct video_device *video_template); +extern int get_format_size(void); + +extern int buffer_setup(struct videobuf_queue *q, unsigned int *count, + unsigned int *size); +extern int buffer_prepare(struct videobuf_queue *q, struct videobuf_buffer *vb, + enum v4l2_field field); +extern void buffer_release(struct videobuf_queue *q, + struct videobuf_buffer *vb); +extern struct videobuf_queue *get_queue(struct cx25821_fh *fh); +extern int get_resource(struct cx25821_fh *fh, int resource); +extern int video_mmap(struct file *file, struct vm_area_struct *vma); +extern int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f); +extern int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap); +extern int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f); +extern int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf); +extern int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p); +extern int vidioc_querybuf(struct file *file, void *priv, + struct v4l2_buffer *p); +extern int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p); +extern int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * tvnorms); +extern int cx25821_enum_input(struct cx25821_dev *dev, struct v4l2_input *i); +extern int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i); +extern int vidioc_g_input(struct file *file, void *priv, unsigned int *i); +extern int vidioc_s_input(struct file *file, void *priv, unsigned int i); +extern int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl); +extern int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f); +extern int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f); +extern int cx25821_set_freq(struct cx25821_dev *dev, struct v4l2_frequency *f); +extern int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f); +extern int vidioc_g_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); +extern int vidioc_s_register(struct file *file, void *fh, + struct v4l2_dbg_register *reg); +extern int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t); +extern int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t); + +extern int is_valid_width(u32 width, v4l2_std_id tvnorm); +extern int is_valid_height(u32 height, v4l2_std_id tvnorm); + +extern int vidioc_g_priority(struct file *file, void *f, enum v4l2_priority *p); +extern int vidioc_s_priority(struct file *file, void *f, + enum v4l2_priority prio); + +extern int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qctrl); +extern int cx25821_set_control(struct cx25821_dev *dev, + struct v4l2_control *ctrl, int chan_num); + +extern int vidioc_cropcap(struct file *file, void *fh, + struct v4l2_cropcap *cropcap); +extern int vidioc_s_crop(struct file *file, void *priv, struct v4l2_crop *crop); +extern int vidioc_g_crop(struct file *file, void *priv, struct v4l2_crop *crop); + +extern int vidioc_querystd(struct file *file, void *priv, v4l2_std_id * norm); +#endif diff --git a/linux/drivers/staging/cx25821/cx25821-video0.c b/linux/drivers/staging/cx25821/cx25821-video0.c new file mode 100644 index 000000000..950fac1d7 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video0.c @@ -0,0 +1,451 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH00]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH00]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH00] + && h->video_dev[SRAM_CH00]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH00; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO0)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO0)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH00]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel0->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO0)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO0); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO0)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO0); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = PIXEL_FRMT_422; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH00, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH00] = 1; + } else { + dev->use_cif_resolution[SRAM_CH00] = 0; + } + dev->cif_width[SRAM_CH00] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH00); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH00].count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH00]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 0 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH00); +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template0 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-video1.c b/linux/drivers/staging/cx25821/cx25821-video1.c new file mode 100644 index 000000000..a4dddc684 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video1.c @@ -0,0 +1,451 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH01]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH01]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH01] + && h->video_dev[SRAM_CH01]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH01; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO1)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO1)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH01]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel1->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO1)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO1); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO1)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO1); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = 0; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH01, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH01] = 1; + } else { + dev->use_cif_resolution[SRAM_CH01] = 0; + } + dev->cif_width[SRAM_CH01] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH01); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH01].count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH01]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 1 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH01); +} + +//exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template1 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-video2.c b/linux/drivers/staging/cx25821/cx25821-video2.c new file mode 100644 index 000000000..8e04e253f --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video2.c @@ -0,0 +1,452 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH02]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH02]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH02] + && h->video_dev[SRAM_CH02]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH02; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO2)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO2)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH02]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel2->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO2)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO2); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO2)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO2); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = 0; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH02, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH02] = 1; + } else { + dev->use_cif_resolution[SRAM_CH02] = 0; + } + dev->cif_width[SRAM_CH02] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH02); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH02].count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH02]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + + cx25821_call_all(dev, core, log_status); + + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 2 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH02); +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template2 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-video3.c b/linux/drivers/staging/cx25821/cx25821-video3.c new file mode 100644 index 000000000..8801a8ead --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video3.c @@ -0,0 +1,451 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH03]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH03]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH03] + && h->video_dev[SRAM_CH03]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH03; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO3)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO3)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH03]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel3->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO3)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO3); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO3)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO3); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = 0; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH03, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH03] = 1; + } else { + dev->use_cif_resolution[SRAM_CH03] = 0; + } + dev->cif_width[SRAM_CH03] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH03); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH03].count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH03]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 3 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH03); +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template3 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-video4.c b/linux/drivers/staging/cx25821/cx25821-video4.c new file mode 100644 index 000000000..ab0d74713 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video4.c @@ -0,0 +1,450 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH04]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH04]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH04] + && h->video_dev[SRAM_CH04]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH04; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO4)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO4)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH04]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel4->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO4)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO4); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO4)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO4); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = 0; + + // check priority + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH04, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH04] = 1; + } else { + dev->use_cif_resolution[SRAM_CH04] = 0; + } + dev->cif_width[SRAM_CH04] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH04); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH04].count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH04]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 4 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH04); +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template4 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-video5.c b/linux/drivers/staging/cx25821/cx25821-video5.c new file mode 100644 index 000000000..7ef0b971f --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video5.c @@ -0,0 +1,450 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH05]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH05]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH05] + && h->video_dev[SRAM_CH05]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH05; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO5)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO5)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH05]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel5->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO5)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO5); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO5)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO5); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = 0; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH05, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH05] = 1; + } else { + dev->use_cif_resolution[SRAM_CH05] = 0; + } + dev->cif_width[SRAM_CH05] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH05); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH05].count; + + return ret_val; +} +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH05]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 5 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH05); +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template5 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-video6.c b/linux/drivers/staging/cx25821/cx25821-video6.c new file mode 100644 index 000000000..3c41b49e2 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video6.c @@ -0,0 +1,450 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH06]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH06]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH06] + && h->video_dev[SRAM_CH06]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH06; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO6)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO6)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH06]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel6->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO6)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO6); + } + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO6)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO6); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = 0; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH06, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH06] = 1; + } else { + dev->use_cif_resolution[SRAM_CH06] = 0; + } + dev->cif_width[SRAM_CH06] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH06); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH06].count; + + return ret_val; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH06]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 6 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH06); +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template6 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-video7.c b/linux/drivers/staging/cx25821/cx25821-video7.c new file mode 100644 index 000000000..625c9b78a --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-video7.c @@ -0,0 +1,449 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH07]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH07]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH07] + && h->video_dev[SRAM_CH07]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = SRAM_CH07; + pix_format = + (dev->pixel_formats[dev->channel_opened] == + PIXEL_FRMT_411) ? V4L2_PIX_FMT_Y41P : V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO7)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO7)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) { + if (buf->vb.state == VIDEOBUF_DONE) { + struct cx25821_dev *dev = fh->dev; + + if (dev && dev->use_cif_resolution[SRAM_CH07]) { + u8 cam_id = *((char *)buf->vb.baddr + 3); + memcpy((char *)buf->vb.baddr, + (char *)buf->vb.baddr + (fh->width * 2), + (fh->width * 2)); + *((char *)buf->vb.baddr + 3) = cam_id; + } + } + + return POLLIN | POLLRDNORM; + } + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + cx_write(channel7->dma_ctl, 0); /* FIFO and RISC disable */ + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO7)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO7); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO7)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO7); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + int pix_format = 0; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->vidq.field = f->fmt.pix.field; + + // check if width and height is valid based on set standard + if (is_valid_width(f->fmt.pix.width, dev->tvnorm)) { + fh->width = f->fmt.pix.width; + } + + if (is_valid_height(f->fmt.pix.height, dev->tvnorm)) { + fh->height = f->fmt.pix.height; + } + + if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_Y41P) + pix_format = PIXEL_FRMT_411; + else if (f->fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) + pix_format = PIXEL_FRMT_422; + else + return -EINVAL; + + cx25821_set_pixel_format(dev, SRAM_CH07, pix_format); + + // check if cif resolution + if (fh->width == 320 || fh->width == 352) { + dev->use_cif_resolution[SRAM_CH07] = 1; + } else { + dev->use_cif_resolution[SRAM_CH07] = 0; + } + dev->cif_width[SRAM_CH07] = fh->width; + medusa_set_resolution(dev, fh->width, SRAM_CH07); + + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int ret_val = 0; + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + + ret_val = videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); + + p->sequence = dev->vidq[SRAM_CH07].count; + + return ret_val; +} +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + struct sram_channel *sram_ch = &dev->sram_channels[SRAM_CH07]; + u32 tmp = 0; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + + tmp = cx_read(sram_ch->dma_ctl); + printk(KERN_INFO "Video input 7 is %s\n", + (tmp & 0x11) ? "streaming" : "stopped"); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return cx25821_set_control(dev, ctl, SRAM_CH07); +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template7 = { + .name = "cx25821-video", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-videoioctl.c b/linux/drivers/staging/cx25821/cx25821-videoioctl.c new file mode 100644 index 000000000..2a312ce78 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-videoioctl.c @@ -0,0 +1,496 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[VIDEO_IOCTL_CH]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[VIDEO_IOCTL_CH]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + u32 pix_format; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->ioctl_dev && h->ioctl_dev->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = VIDEO_IOCTL_CH; + pix_format = V4L2_PIX_FMT_YUYV; + fh->fmt = format_by_fourcc(pix_format); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO_IOCTL)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO_IOCTL)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO_IOCTL)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO_IOCTL); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO_IOCTL)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO_IOCTL); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vidq.field = f->fmt.pix.field; + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); +} + +static long video_ioctl_set(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + struct downstream_user_struct *data_from_user; + int command; + int width = 720; + int selected_channel = 0, pix_format = 0, i = 0; + int cif_enable = 0, cif_width = 0; + u32 value = 0; + + data_from_user = (struct downstream_user_struct *)arg; + + if (!data_from_user) { + printk("cx25821 in %s(): User data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != SET_VIDEO_STD && command != SET_PIXEL_FORMAT + && command != ENABLE_CIF_RESOLUTION && command != REG_READ + && command != REG_WRITE && command != MEDUSA_READ + && command != MEDUSA_WRITE) { + return 0; + } + + switch (command) { + case SET_VIDEO_STD: + dev->tvnorm = + !strcmp(data_from_user->vid_stdname, + "PAL") ? V4L2_STD_PAL_BG : V4L2_STD_NTSC_M; + medusa_set_videostandard(dev); + break; + + case SET_PIXEL_FORMAT: + selected_channel = data_from_user->decoder_select; + pix_format = data_from_user->pixel_format; + + if (!(selected_channel <= 7 && selected_channel >= 0)) { + selected_channel -= 4; + selected_channel = selected_channel % 8; + } + + if (selected_channel >= 0) + cx25821_set_pixel_format(dev, selected_channel, + pix_format); + + break; + + case ENABLE_CIF_RESOLUTION: + selected_channel = data_from_user->decoder_select; + cif_enable = data_from_user->cif_resolution_enable; + cif_width = data_from_user->cif_width; + + if (cif_enable) { + if (dev->tvnorm & V4L2_STD_PAL_BG + || dev->tvnorm & V4L2_STD_PAL_DK) + width = 352; + else + width = (cif_width == 320 + || cif_width == 352) ? cif_width : 320; + } + + if (!(selected_channel <= 7 && selected_channel >= 0)) { + selected_channel -= 4; + selected_channel = selected_channel % 8; + } + + if (selected_channel <= 7 && selected_channel >= 0) { + dev->use_cif_resolution[selected_channel] = cif_enable; + dev->cif_width[selected_channel] = width; + } else { + for (i = 0; i < VID_CHANNEL_NUM; i++) { + dev->use_cif_resolution[i] = cif_enable; + dev->cif_width[i] = width; + } + } + + medusa_set_resolution(dev, width, selected_channel); + break; + case REG_READ: + data_from_user->reg_data = cx_read(data_from_user->reg_address); + break; + case REG_WRITE: + cx_write(data_from_user->reg_address, data_from_user->reg_data); + break; + case MEDUSA_READ: + value = + cx25821_i2c_read(&dev->i2c_bus[0], + (u16) data_from_user->reg_address, + &data_from_user->reg_data); + break; + case MEDUSA_WRITE: + cx25821_i2c_write(&dev->i2c_bus[0], + (u16) data_from_user->reg_address, + data_from_user->reg_data); + break; + } + + return 0; +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return 0; +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl_set, +}; + +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, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_videoioctl_template = { + .name = "cx25821-videoioctl", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-vidups10.c b/linux/drivers/staging/cx25821/cx25821-vidups10.c new file mode 100644 index 000000000..77b63b060 --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-vidups10.c @@ -0,0 +1,435 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH10]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH10]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH10] + && h->video_dev[SRAM_CH10]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = 9; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO10)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO10)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + //cx_write(channel10->dma_ctl, 0); + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO10)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO10); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO10)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO10); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static long video_ioctl_upstream10(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + printk + ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) { + return 0; + } + + dev->input_filename_ch2 = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname_ch2 = data_from_user->vid_stdname; + dev->pixel_format_ch2 = data_from_user->pixel_format; + dev->channel_select_ch2 = data_from_user->channel_select; + dev->command_ch2 = data_from_user->command; + + switch (command) { + case UPSTREAM_START_VIDEO: + cx25821_start_upstream_video_ch2(dev, data_from_user); + break; + + case UPSTREAM_STOP_VIDEO: + cx25821_stop_upstream_video_ch2(dev); + break; + } + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vidq.field = f->fmt.pix.field; + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); +} + +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return 0; +} + +//exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl_upstream10, +}; + +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, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template10 = { + .name = "cx25821-upstream10", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821-vidups9.c b/linux/drivers/staging/cx25821/cx25821-vidups9.c new file mode 100644 index 000000000..75c8c1eed --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821-vidups9.c @@ -0,0 +1,433 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cx25821-video.h" + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct cx25821_buffer *buf = + container_of(vb, struct cx25821_buffer, vb); + struct cx25821_buffer *prev; + struct cx25821_fh *fh = vq->priv_data; + struct cx25821_dev *dev = fh->dev; + struct cx25821_dmaqueue *q = &dev->vidq[SRAM_CH09]; + + /* add jump to stopper */ + buf->risc.jmp[0] = cpu_to_le32(RISC_JUMP | RISC_IRQ1 | RISC_CNT_INC); + buf->risc.jmp[1] = cpu_to_le32(q->stopper.dma); + buf->risc.jmp[2] = cpu_to_le32(0); /* bits 63-32 */ + + dprintk(2, "jmp to stopper (0x%x)\n", buf->risc.jmp[1]); + + if (!list_empty(&q->queued)) { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - append to queued\n", buf, + buf->vb.i); + + } else if (list_empty(&q->active)) { + list_add_tail(&buf->vb.queue, &q->active); + cx25821_start_video_dma(dev, q, buf, + &dev->sram_channels[SRAM_CH09]); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + mod_timer(&q->timeout, jiffies + BUFFER_TIMEOUT); + dprintk(2, + "[%p/%d] buffer_queue - first active, buf cnt = %d, q->count = %d\n", + buf, buf->vb.i, buf->count, q->count); + } else { + prev = + list_entry(q->active.prev, struct cx25821_buffer, vb.queue); + if (prev->vb.width == buf->vb.width + && prev->vb.height == buf->vb.height + && prev->fmt == buf->fmt) { + list_add_tail(&buf->vb.queue, &q->active); + buf->vb.state = VIDEOBUF_ACTIVE; + buf->count = q->count++; + prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma); + + /* 64 bit bits 63-32 */ + prev->risc.jmp[2] = cpu_to_le32(0); + dprintk(2, + "[%p/%d] buffer_queue - append to active, buf->count=%d\n", + buf, buf->vb.i, buf->count); + + } else { + list_add_tail(&buf->vb.queue, &q->queued); + buf->vb.state = VIDEOBUF_QUEUED; + dprintk(2, "[%p/%d] buffer_queue - first queued\n", buf, + buf->vb.i); + } + } + + if (list_empty(&q->active)) { + dprintk(2, "active queue empty!\n"); + } +} + +static struct videobuf_queue_ops cx25821_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +static int video_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct cx25821_dev *h, *dev = NULL; + struct cx25821_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + + lock_kernel(); + list_for_each(list, &cx25821_devlist) { + h = list_entry(list, struct cx25821_dev, devlist); + + if (h->video_dev[SRAM_CH09] + && h->video_dev[SRAM_CH09]->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + + if (NULL == dev) { + unlock_kernel(); + return -ENODEV; + } + + printk("open minor=%d type=%s\n", minor, v4l2_type_names[type]); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) { + unlock_kernel(); + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + fh->type = type; + fh->width = 720; + + if (dev->tvnorm & V4L2_STD_PAL_BG || dev->tvnorm & V4L2_STD_PAL_DK) + fh->height = 576; + else + fh->height = 480; + + dev->channel_opened = 8; + fh->fmt = format_by_fourcc(V4L2_PIX_FMT_YUYV); + + v4l2_prio_open(&dev->prio, &fh->prio); + + videobuf_queue_sg_init(&fh->vidq, &cx25821_video_qops, + &dev->pci->dev, &dev->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_INTERLACED, + sizeof(struct cx25821_buffer), fh); + + dprintk(1, "post videobuf_queue_init()\n"); + unlock_kernel(); + + return 0; +} + +static ssize_t video_read(struct file *file, char __user * data, size_t count, + loff_t * ppos) +{ + struct cx25821_fh *fh = file->private_data; + + switch (fh->type) { + case V4L2_BUF_TYPE_VIDEO_CAPTURE: + if (res_locked(fh->dev, RESOURCE_VIDEO9)) + return -EBUSY; + + return videobuf_read_one(&fh->vidq, data, count, ppos, + file->f_flags & O_NONBLOCK); + + default: + BUG(); + return 0; + } +} + +static unsigned int video_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_buffer *buf; + + if (res_check(fh, RESOURCE_VIDEO9)) { + /* streaming capture */ + if (list_empty(&fh->vidq.stream)) + return POLLERR; + buf = list_entry(fh->vidq.stream.next, + struct cx25821_buffer, vb.stream); + } else { + /* read() capture */ + buf = (struct cx25821_buffer *)fh->vidq.read_buf; + if (NULL == buf) + return POLLERR; + } + + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) + return POLLIN | POLLRDNORM; + return 0; +} + +static int video_release(struct file *file) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + + //stop the risc engine and fifo + //cx_write(channel9->dma_ctl, 0); + + /* stop video capture */ + if (res_check(fh, RESOURCE_VIDEO9)) { + videobuf_queue_cancel(&fh->vidq); + res_free(dev, fh, RESOURCE_VIDEO9); + } + + if (fh->vidq.read_buf) { + buffer_release(&fh->vidq, fh->vidq.read_buf); + kfree(fh->vidq.read_buf); + } + + videobuf_mmap_free(&fh->vidq); + + v4l2_prio_close(&dev->prio, &fh->prio); + + file->private_data = NULL; + kfree(fh); + + return 0; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + + if (unlikely(fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)) { + return -EINVAL; + } + + if (unlikely(i != fh->type)) { + return -EINVAL; + } + + if (unlikely(!res_get(dev, fh, get_resource(fh, RESOURCE_VIDEO9)))) { + return -EBUSY; + } + + return videobuf_streamon(get_queue(fh)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = fh->dev; + int err, res; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + res = get_resource(fh, RESOURCE_VIDEO9); + err = videobuf_streamoff(get_queue(fh)); + if (err < 0) + return err; + res_free(dev, fh, res); + return 0; +} + +static long video_ioctl_upstream9(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct cx25821_fh *fh = file->private_data; + struct cx25821_dev *dev = fh->dev; + int command = 0; + struct upstream_user_struct *data_from_user; + + data_from_user = (struct upstream_user_struct *)arg; + + if (!data_from_user) { + printk + ("cx25821 in %s(): Upstream data is INVALID. Returning.\n", + __func__); + return 0; + } + + command = data_from_user->command; + + if (command != UPSTREAM_START_VIDEO && command != UPSTREAM_STOP_VIDEO) { + return 0; + } + + dev->input_filename = data_from_user->input_filename; + dev->input_audiofilename = data_from_user->input_filename; + dev->vid_stdname = data_from_user->vid_stdname; + dev->pixel_format = data_from_user->pixel_format; + dev->channel_select = data_from_user->channel_select; + dev->command = data_from_user->command; + + switch (command) { + case UPSTREAM_START_VIDEO: + cx25821_start_upstream_video_ch1(dev, data_from_user); + break; + + case UPSTREAM_STOP_VIDEO: + cx25821_stop_upstream_video_ch1(dev); + break; + } + + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct cx25821_fh *fh = priv; + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + int err; + + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + dprintk(2, "%s()\n", __func__); + err = vidioc_try_fmt_vid_cap(file, priv, f); + + if (0 != err) + return err; + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vidq.field = f->fmt.pix.field; + dprintk(2, "%s() width=%d height=%d field=%d\n", __func__, fh->width, + fh->height, fh->vidq.field); + cx25821_call_all(dev, video, s_fmt, f); + return 0; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct cx25821_fh *fh = priv; + return videobuf_dqbuf(get_queue(fh), p, file->f_flags & O_NONBLOCK); +} +static int vidioc_log_status(struct file *file, void *priv) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + char name[32 + 2]; + + snprintf(name, sizeof(name), "%s/2", dev->name); + printk(KERN_INFO "%s/2: ============ START LOG STATUS ============\n", + dev->name); + cx25821_call_all(dev, core, log_status); + printk(KERN_INFO "%s/2: ============= END LOG STATUS =============\n", + dev->name); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctl) +{ + struct cx25821_dev *dev = ((struct cx25821_fh *)priv)->dev; + struct cx25821_fh *fh = priv; + int err; + if (fh) { + err = v4l2_prio_check(&dev->prio, &fh->prio); + if (0 != err) + return err; + } + + return 0; +} + +// exported stuff +static const struct v4l2_file_operations video_fops = { + .owner = THIS_MODULE, + .open = video_open, + .release = video_release, + .read = video_read, + .poll = video_poll, + .mmap = video_mmap, + .ioctl = video_ioctl_upstream9, +}; + +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, +#ifdef TUNER_FLAG + .vidioc_s_std = vidioc_s_std, + .vidioc_querystd = vidioc_querystd, +#endif + .vidioc_cropcap = vidioc_cropcap, + .vidioc_s_crop = vidioc_s_crop, + .vidioc_g_crop = vidioc_g_crop, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_log_status = vidioc_log_status, + .vidioc_g_priority = vidioc_g_priority, + .vidioc_s_priority = vidioc_s_priority, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +#ifdef TUNER_FLAG + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, +#endif +#ifdef CONFIG_VIDEO_ADV_DEBUG + .vidioc_g_register = vidioc_g_register, + .vidioc_s_register = vidioc_s_register, +#endif +}; + +struct video_device cx25821_video_template9 = { + .name = "cx25821-upstream9", + .fops = &video_fops, + .minor = -1, + .ioctl_ops = &video_ioctl_ops, + .tvnorms = CX25821_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; diff --git a/linux/drivers/staging/cx25821/cx25821.h b/linux/drivers/staging/cx25821/cx25821.h new file mode 100644 index 000000000..c1fad805e --- /dev/null +++ b/linux/drivers/staging/cx25821/cx25821.h @@ -0,0 +1,603 @@ +/* + * Driver for the Conexant CX25821 PCIe bridge + * + * Copyright (C) 2009 Conexant Systems Inc. + * Authors <shu.lin@conexant.com>, <hiep.huynh@conexant.com> + * Based on Steven Toth <stoth@linuxtv.org> cx23885 driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef CX25821_H_ +#define CX25821_H_ + +#include <linux/pci.h> +#include <linux/i2c.h> +#include <linux/i2c-algo-bit.h> +#include <linux/interrupt.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/kdev_t.h> +#include <linux/smp_lock.h> + +#include <media/v4l2-common.h> +#include <media/v4l2-device.h> +#include <media/tuner.h> +#include <media/tveeprom.h> +#include <media/videobuf-dma-sg.h> +#include <media/videobuf-dvb.h> +#include "compat.h" + +#include "btcx-risc.h" +#include "cx25821-reg.h" +#include "cx25821-medusa-reg.h" +#include "cx25821-sram.h" +#include "cx25821-audio.h" +#include "media/cx2341x.h" + +#include <linux/version.h> +#include <linux/mutex.h> + +#define CX25821_VERSION_CODE KERNEL_VERSION(0, 0, 106) + +#define UNSET (-1U) +#define NO_SYNC_LINE (-1U) + +#define CX25821_MAXBOARDS 2 + +#define TRUE 1 +#define FALSE 0 +#define LINE_SIZE_D1 1440 + +// Number of decoders and encoders +#define MAX_DECODERS 8 +#define MAX_ENCODERS 2 +#define QUAD_DECODERS 4 +#define MAX_CAMERAS 16 + +/* Max number of inputs by card */ +#define MAX_CX25821_INPUT 8 +#define INPUT(nr) (&cx25821_boards[dev->board].input[nr]) +#define RESOURCE_VIDEO0 1 +#define RESOURCE_VIDEO1 2 +#define RESOURCE_VIDEO2 4 +#define RESOURCE_VIDEO3 8 +#define RESOURCE_VIDEO4 16 +#define RESOURCE_VIDEO5 32 +#define RESOURCE_VIDEO6 64 +#define RESOURCE_VIDEO7 128 +#define RESOURCE_VIDEO8 256 +#define RESOURCE_VIDEO9 512 +#define RESOURCE_VIDEO10 1024 +#define RESOURCE_VIDEO11 2048 +#define RESOURCE_VIDEO_IOCTL 4096 + +#define BUFFER_TIMEOUT (HZ) /* 0.5 seconds */ + +#define UNKNOWN_BOARD 0 +#define CX25821_BOARD 1 + +/* Currently supported by the driver */ +#define CX25821_NORMS (\ + V4L2_STD_NTSC_M | V4L2_STD_NTSC_M_JP | V4L2_STD_NTSC_M_KR | \ + V4L2_STD_PAL_BG | V4L2_STD_PAL_DK | V4L2_STD_PAL_I | \ + V4L2_STD_PAL_M | V4L2_STD_PAL_N | V4L2_STD_PAL_H | \ + V4L2_STD_PAL_Nc ) + +#define CX25821_BOARD_CONEXANT_ATHENA10 1 +#define MAX_VID_CHANNEL_NUM 12 +#define VID_CHANNEL_NUM 8 + +struct cx25821_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; + int flags; + u32 cxformat; +}; + +struct cx25821_ctrl { + struct v4l2_queryctrl v; + u32 off; + u32 reg; + u32 mask; + u32 shift; +}; + +struct cx25821_tvnorm { + char *name; + v4l2_std_id id; + u32 cxiformat; + u32 cxoformat; +}; + +struct cx25821_fh { + struct cx25821_dev *dev; + enum v4l2_buf_type type; + int radio; + u32 resources; + + enum v4l2_priority prio; + + /* video overlay */ + struct v4l2_window win; + struct v4l2_clip *clips; + unsigned int nclips; + + /* video capture */ + struct cx25821_fmt *fmt; + unsigned int width, height; + + /* vbi capture */ + struct videobuf_queue vidq; + struct videobuf_queue vbiq; + + /* H264 Encoder specifics ONLY */ + struct videobuf_queue mpegq; + atomic_t v4l_reading; +}; + +enum cx25821_itype { + CX25821_VMUX_COMPOSITE = 1, + CX25821_VMUX_SVIDEO, + CX25821_VMUX_DEBUG, + CX25821_RADIO, +}; + +enum cx25821_src_sel_type { + CX25821_SRC_SEL_EXT_656_VIDEO = 0, + CX25821_SRC_SEL_PARALLEL_MPEG_VIDEO +}; + +/* buffer for one video frame */ +struct cx25821_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + /* cx25821 specific */ + unsigned int bpl; + struct btcx_riscmem risc; + struct cx25821_fmt *fmt; + u32 count; +}; + +struct cx25821_input { + enum cx25821_itype type; + unsigned int vmux; + u32 gpio0, gpio1, gpio2, gpio3; +}; + +typedef enum { + CX25821_UNDEFINED = 0, + CX25821_RAW, + CX25821_264 +} port_t; + +struct cx25821_board { + char *name; + port_t porta, portb, portc; + unsigned int tuner_type; + unsigned int radio_type; + unsigned char tuner_addr; + unsigned char radio_addr; + + u32 clk_freq; + struct cx25821_input input[2]; +}; + +struct cx25821_subid { + u16 subvendor; + u16 subdevice; + u32 card; +}; + +struct cx25821_i2c { + struct cx25821_dev *dev; + + int nr; + + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_algo_bit_data i2c_algo; + struct i2c_client i2c_client; + u32 i2c_rc; + + /* cx25821 registers used for raw addess */ + u32 i2c_period; + u32 reg_ctrl; + u32 reg_stat; + u32 reg_addr; + u32 reg_rdata; + u32 reg_wdata; +}; + +struct cx25821_dmaqueue { + struct list_head active; + struct list_head queued; + struct timer_list timeout; + struct btcx_riscmem stopper; + u32 count; +}; + +struct cx25821_data { + struct cx25821_dev *dev; + struct sram_channel *channel; +}; + +struct cx25821_dev { + struct list_head devlist; + atomic_t refcount; + struct v4l2_device v4l2_dev; + + struct v4l2_prio_state prio; + + /* pci stuff */ + struct pci_dev *pci; + unsigned char pci_rev, pci_lat; + int pci_bus, pci_slot; + u32 base_io_addr; + u32 __iomem *lmmio; + u8 __iomem *bmmio; + int pci_irqmask; + int hwrevision; + + u32 clk_freq; + + /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */ + struct cx25821_i2c i2c_bus[3]; + + int nr; + struct mutex lock; + + /* board details */ + unsigned int board; + char name[32]; + + /* sram configuration */ + struct sram_channel *sram_channels; + + /* Analog video */ + u32 resources; + unsigned int input; + u32 tvaudio; + v4l2_std_id tvnorm; + unsigned int tuner_type; + unsigned char tuner_addr; + unsigned int radio_type; + unsigned char radio_addr; + unsigned int has_radio; + unsigned int videc_type; + unsigned char videc_addr; + unsigned short _max_num_decoders; + + int ctl_bright; + int ctl_contrast; + int ctl_hue; + int ctl_saturation; + + struct cx25821_data timeout_data[MAX_VID_CHANNEL_NUM]; + + /* Analog Audio Upstream */ + int _audio_is_running; + int _audiopixel_format; + int _is_first_audio_frame; + int _audiofile_status; + int _audio_lines_count; + int _audioframe_count; + int _audio_upstream_channel_select; + int _last_index_irq; //The last interrupt index processed. + + __le32 *_risc_audio_jmp_addr; + __le32 *_risc_virt_start_addr; + __le32 *_risc_virt_addr; + dma_addr_t _risc_phys_addr; + dma_addr_t _risc_phys_start_addr; + + unsigned int _audiorisc_size; + unsigned int _audiodata_buf_size; + __le32 *_audiodata_buf_virt_addr; + dma_addr_t _audiodata_buf_phys_addr; + char *_audiofilename; + + /* V4l */ + u32 freq; + struct video_device *video_dev[MAX_VID_CHANNEL_NUM]; + struct video_device *vbi_dev; + struct video_device *radio_dev; + struct video_device *ioctl_dev; + + struct cx25821_dmaqueue vidq[MAX_VID_CHANNEL_NUM]; + spinlock_t slock; + + /* Video Upstream */ + int _line_size; + int _prog_cnt; + int _pixel_format; + int _is_first_frame; + int _is_running; + int _file_status; + int _lines_count; + int _frame_count; + int _channel_upstream_select; + unsigned int _risc_size; + + __le32 *_dma_virt_start_addr; + __le32 *_dma_virt_addr; + dma_addr_t _dma_phys_addr; + dma_addr_t _dma_phys_start_addr; + + unsigned int _data_buf_size; + __le32 *_data_buf_virt_addr; + dma_addr_t _data_buf_phys_addr; + char *_filename; + char *_defaultname; + + int _line_size_ch2; + int _prog_cnt_ch2; + int _pixel_format_ch2; + int _is_first_frame_ch2; + int _is_running_ch2; + int _file_status_ch2; + int _lines_count_ch2; + int _frame_count_ch2; + int _channel2_upstream_select; + unsigned int _risc_size_ch2; + + __le32 *_dma_virt_start_addr_ch2; + __le32 *_dma_virt_addr_ch2; + dma_addr_t _dma_phys_addr_ch2; + dma_addr_t _dma_phys_start_addr_ch2; + + unsigned int _data_buf_size_ch2; + __le32 *_data_buf_virt_addr_ch2; + dma_addr_t _data_buf_phys_addr_ch2; + char *_filename_ch2; + char *_defaultname_ch2; + + /* MPEG Encoder ONLY settings */ + u32 cx23417_mailbox; + struct cx2341x_mpeg_params mpeg_params; + struct video_device *v4l_device; + atomic_t v4l_reader_count; + struct cx25821_tvnorm encodernorm; + + u32 upstream_riscbuf_size; + u32 upstream_databuf_size; + u32 upstream_riscbuf_size_ch2; + u32 upstream_databuf_size_ch2; + u32 audio_upstream_riscbuf_size; + u32 audio_upstream_databuf_size; + int _isNTSC; + int _frame_index; + int _audioframe_index; + struct workqueue_struct *_irq_queues; + struct work_struct _irq_work_entry; + struct workqueue_struct *_irq_queues_ch2; + struct work_struct _irq_work_entry_ch2; + struct workqueue_struct *_irq_audio_queues; + struct work_struct _audio_work_entry; + char *input_filename; + char *input_filename_ch2; + int _frame_index_ch2; + int _isNTSC_ch2; + char *vid_stdname_ch2; + int pixel_format_ch2; + int channel_select_ch2; + int command_ch2; + char *input_audiofilename; + char *vid_stdname; + int pixel_format; + int channel_select; + int command; + int pixel_formats[VID_CHANNEL_NUM]; + int use_cif_resolution[VID_CHANNEL_NUM]; + int cif_width[VID_CHANNEL_NUM]; + int channel_opened; +}; + +struct upstream_user_struct { + char *input_filename; + char *vid_stdname; + int pixel_format; + int channel_select; + int command; +}; + +struct downstream_user_struct { + char *vid_stdname; + int pixel_format; + int cif_resolution_enable; + int cif_width; + int decoder_select; + int command; + int reg_address; + int reg_data; +}; + +extern struct upstream_user_struct *up_data; + +static inline struct cx25821_dev *get_cx25821(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct cx25821_dev, v4l2_dev); +} + +#define cx25821_call_all(dev, o, f, args...) \ + v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args) + +extern struct list_head cx25821_devlist; +extern struct cx25821_board cx25821_boards[]; +extern struct cx25821_subid cx25821_subids[]; + +#define SRAM_CH00 0 /* Video A */ +#define SRAM_CH01 1 /* Video B */ +#define SRAM_CH02 2 /* Video C */ +#define SRAM_CH03 3 /* Video D */ +#define SRAM_CH04 4 /* Video E */ +#define SRAM_CH05 5 /* Video F */ +#define SRAM_CH06 6 /* Video G */ +#define SRAM_CH07 7 /* Video H */ + +#define SRAM_CH08 8 /* Audio A */ +#define SRAM_CH09 9 /* Video Upstream I */ +#define SRAM_CH10 10 /* Video Upstream J */ +#define SRAM_CH11 11 /* Audio Upstream AUD_CHANNEL_B */ + +#define VID_UPSTREAM_SRAM_CHANNEL_I SRAM_CH09 +#define VID_UPSTREAM_SRAM_CHANNEL_J SRAM_CH10 +#define AUDIO_UPSTREAM_SRAM_CHANNEL_B SRAM_CH11 +#define VIDEO_IOCTL_CH 11 + +struct sram_channel { + char *name; + u32 i; + u32 cmds_start; + u32 ctrl_start; + u32 cdt; + u32 fifo_start; + u32 fifo_size; + u32 ptr1_reg; + u32 ptr2_reg; + u32 cnt1_reg; + u32 cnt2_reg; + u32 int_msk; + u32 int_stat; + u32 int_mstat; + u32 dma_ctl; + u32 gpcnt_ctl; + u32 gpcnt; + u32 aud_length; + u32 aud_cfg; + u32 fld_aud_fifo_en; + u32 fld_aud_risc_en; + + //For Upstream Video + u32 vid_fmt_ctl; + u32 vid_active_ctl1; + u32 vid_active_ctl2; + u32 vid_cdt_size; + + u32 vip_ctl; + u32 pix_frmt; + u32 jumponly; + u32 irq_bit; +}; +extern struct sram_channel cx25821_sram_channels[]; + +#define STATUS_SUCCESS 0 +#define STATUS_UNSUCCESSFUL -1 + +#define cx_read(reg) readl(dev->lmmio + ((reg)>>2)) +#define cx_write(reg, value) writel((value), dev->lmmio + ((reg)>>2)) + +#define cx_andor(reg, mask, value) \ + writel((readl(dev->lmmio+((reg)>>2)) & ~(mask)) |\ + ((value) & (mask)), dev->lmmio+((reg)>>2)) + +#define cx_set(reg, bit) cx_andor((reg), (bit), (bit)) +#define cx_clear(reg, bit) cx_andor((reg), (bit), 0) + +#define Set_GPIO_Bit(Bit) (1 << Bit) +#define Clear_GPIO_Bit(Bit) (~(1 << Bit)) + +#define CX25821_ERR(fmt, args...) printk(KERN_ERR "cx25821(%d): " fmt, dev->board, ## args) +#define CX25821_WARN(fmt, args...) printk(KERN_WARNING "cx25821(%d): " fmt, dev->board , ## args) +#define CX25821_INFO(fmt, args...) printk(KERN_INFO "cx25821(%d): " fmt, dev->board , ## args) + +extern int cx25821_i2c_register(struct cx25821_i2c *bus); +extern void cx25821_card_setup(struct cx25821_dev *dev); +extern int cx25821_ir_init(struct cx25821_dev *dev); +extern int cx25821_i2c_read(struct cx25821_i2c *bus, u16 reg_addr, int *value); +extern int cx25821_i2c_write(struct cx25821_i2c *bus, u16 reg_addr, int value); +extern int cx25821_i2c_unregister(struct cx25821_i2c *bus); +extern void cx25821_gpio_init(struct cx25821_dev *dev); +extern void cx25821_set_gpiopin_direction(struct cx25821_dev *dev, + int pin_number, int pin_logic_value); + +extern int medusa_video_init(struct cx25821_dev *dev); +extern int medusa_set_videostandard(struct cx25821_dev *dev); +extern void medusa_set_resolution(struct cx25821_dev *dev, int width, + int decoder_select); +extern int medusa_set_brightness(struct cx25821_dev *dev, int brightness, + int decoder); +extern int medusa_set_contrast(struct cx25821_dev *dev, int contrast, + int decoder); +extern int medusa_set_hue(struct cx25821_dev *dev, int hue, int decoder); +extern int medusa_set_saturation(struct cx25821_dev *dev, int saturation, + int decoder); + +extern int cx25821_sram_channel_setup(struct cx25821_dev *dev, + struct sram_channel *ch, unsigned int bpl, + u32 risc); + +extern int cx25821_risc_buffer(struct pci_dev *pci, struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int top_offset, + unsigned int bottom_offset, + unsigned int bpl, + unsigned int padding, unsigned int lines); +extern int cx25821_risc_databuffer_audio(struct pci_dev *pci, + struct btcx_riscmem *risc, + struct scatterlist *sglist, + unsigned int bpl, + unsigned int lines, unsigned int lpi); +extern void cx25821_free_buffer(struct videobuf_queue *q, + struct cx25821_buffer *buf); +extern int cx25821_risc_stopper(struct pci_dev *pci, struct btcx_riscmem *risc, + u32 reg, u32 mask, u32 value); +extern void cx25821_sram_channel_dump(struct cx25821_dev *dev, + struct sram_channel *ch); +extern void cx25821_sram_channel_dump_audio(struct cx25821_dev *dev, + struct sram_channel *ch); + +extern struct cx25821_dev *cx25821_dev_get(struct pci_dev *pci); +extern void cx25821_print_irqbits(char *name, char *tag, char **strings, + int len, u32 bits, u32 mask); +extern void cx25821_dev_unregister(struct cx25821_dev *dev); +extern int cx25821_sram_channel_setup_audio(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc); + +extern int cx25821_vidupstream_init_ch1(struct cx25821_dev *dev, + int channel_select, int pixel_format); +extern int cx25821_vidupstream_init_ch2(struct cx25821_dev *dev, + int channel_select, int pixel_format); +extern int cx25821_audio_upstream_init(struct cx25821_dev *dev, + int channel_select); +extern void cx25821_free_mem_upstream_ch1(struct cx25821_dev *dev); +extern void cx25821_free_mem_upstream_ch2(struct cx25821_dev *dev); +extern void cx25821_free_mem_upstream_audio(struct cx25821_dev *dev); +extern void cx25821_start_upstream_video_ch1(struct cx25821_dev *dev, + struct upstream_user_struct + *up_data); +extern void cx25821_start_upstream_video_ch2(struct cx25821_dev *dev, + struct upstream_user_struct + *up_data); +extern void cx25821_start_upstream_audio(struct cx25821_dev *dev, + struct upstream_user_struct *up_data); +extern void cx25821_stop_upstream_video_ch1(struct cx25821_dev *dev); +extern void cx25821_stop_upstream_video_ch2(struct cx25821_dev *dev); +extern void cx25821_stop_upstream_audio(struct cx25821_dev *dev); +extern int cx25821_sram_channel_setup_upstream(struct cx25821_dev *dev, + struct sram_channel *ch, + unsigned int bpl, u32 risc); +extern void cx25821_set_pixel_format(struct cx25821_dev *dev, int channel, + u32 format); +extern void cx25821_videoioctl_unregister(struct cx25821_dev *dev); +extern struct video_device *cx25821_vdev_init(struct cx25821_dev *dev, + struct pci_dev *pci, + struct video_device *template, + char *type); +#endif diff --git a/linux/drivers/staging/go7007/Kconfig b/linux/drivers/staging/go7007/Kconfig new file mode 100644 index 000000000..ca6ade6c4 --- /dev/null +++ b/linux/drivers/staging/go7007/Kconfig @@ -0,0 +1,37 @@ +config VIDEO_GO7007 + tristate "Go 7007 support" + depends on VIDEO_DEV && PCI && I2C && INPUT + depends on SND + select VIDEOBUF_DMA_SG + select VIDEO_IR + select VIDEO_TUNER + select VIDEO_TVEEPROM + select SND_PCM + select CRC32 + default N + ---help--- + This is a video4linux driver for some weird device... + + To compile this driver as a module, choose M here: the + module will be called go7007 + +config VIDEO_GO7007_USB + tristate "Go 7007 USB support" + depends on VIDEO_GO7007 && USB + default N + ---help--- + This is a video4linux driver for some weird device... + + 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-board + diff --git a/linux/drivers/staging/go7007/Makefile b/linux/drivers/staging/go7007/Makefile new file mode 100644 index 000000000..e514b4af6 --- /dev/null +++ b/linux/drivers/staging/go7007/Makefile @@ -0,0 +1,27 @@ +#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 + +go7007-objs += go7007-v4l2.o go7007-driver.o go7007-i2c.o go7007-fw.o \ + snd-go7007.o wis-saa7113.o + +s2250-objs += s2250-board.o s2250-loader.o + +# Uncompile when the saa7134 patches get into upstream +#ifneq ($(CONFIG_VIDEO_SAA7134),) +#obj-$(CONFIG_VIDEO_SAA7134) += saa7134-go7007.o +#EXTRA_CFLAGS += -Idrivers/media/video/saa7134 +#endif + +ifneq ($(CONFIG_VIDEO_GO7007_USB_S2250_BOARD),) +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-usb +endif + +EXTRA_CFLAGS += -Idrivers/staging/saa7134 +EXTRA_CFLAGS += -Idrivers/media/dvb/frontends +EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core diff --git a/linux/drivers/staging/go7007/README b/linux/drivers/staging/go7007/README new file mode 100644 index 000000000..48f447637 --- /dev/null +++ b/linux/drivers/staging/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 <greg@kroah.com> and Cc: Ross +Cohen <rcohen@snurgle.org> as well. + diff --git a/linux/drivers/staging/go7007/go7007-driver.c b/linux/drivers/staging/go7007/go7007-driver.c new file mode 100644 index 000000000..359a34f67 --- /dev/null +++ b/linux/drivers/staging/go7007/go7007-driver.c @@ -0,0 +1,682 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/unistd.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/vmalloc.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <asm/system.h> +#include <linux/videodev2.h> +#include <media/tuner.h> +#include <media/v4l2-common.h> + +#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) { + printk(KERN_ERR "go7007: 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)) { + printk(KERN_ERR + "go7007: unable to load firmware from file \"%s\"\n", + fw_name); + return -1; + } + if (fw_entry->size < 16 || memcmp(fw_entry->data, "WISGO7007FW", 11)) { + printk(KERN_ERR "go7007: 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) { + printk(KERN_ERR "go7007: 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) { + printk(KERN_ERR "go7007: error transferring firmware\n"); + rv = -1; + } + kfree(bounce); + return rv; +} + +/* + * 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 id, int addr) +{ + struct i2c_board_info info; + char *modname; + + switch (id) { + case I2C_DRIVERID_WIS_SAA7115: + modname = "wis-saa7115"; + break; + case I2C_DRIVERID_WIS_SAA7113: + modname = "wis-saa7113"; + break; + case I2C_DRIVERID_WIS_UDA1342: + modname = "wis-uda1342"; + break; + case I2C_DRIVERID_WIS_SONY_TUNER: + modname = "wis-sony-tuner"; + break; + case I2C_DRIVERID_WIS_TW9903: + modname = "wis-tw9903"; + break; + case I2C_DRIVERID_WIS_TW2804: + modname = "wis-tw2804"; + break; + case I2C_DRIVERID_WIS_OV7640: + modname = "wis-ov7640"; + break; + case I2C_DRIVERID_S2250: + modname = "s2250-board"; + break; + default: + modname = NULL; + break; + } + if (modname != NULL) + request_module(modname); + + memset(&info, 0, sizeof(struct i2c_board_info)); + info.addr = addr; + strlcpy(info.type, type, I2C_NAME_SIZE); + if (!i2c_new_device(adapter, &info)) + return 0; + if (modname != NULL) + printk(KERN_INFO + "go7007: probing for module %s failed\n", modname); + else + printk(KERN_INFO + "go7007: sensor %u seems to be unsupported!\n", id); + 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; + + 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].id, + 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 go7007_v4l2_init(go); +} +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) { + printk(KERN_ERR "go7007: 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) { + printk(KERN_ERR "go7007: 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); + 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) { + printk(KERN_DEBUG "go7007: 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 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 + printk(KERN_ERR + "go7007: 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/linux/drivers/staging/go7007/go7007-fw.c b/linux/drivers/staging/go7007/go7007-fw.c new file mode 100644 index 000000000..73d867024 --- /dev/null +++ b/linux/drivers/staging/go7007/go7007-fw.c @@ -0,0 +1,1639 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/firmware.h> +#include <asm/byteorder.h> + +#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 = kmalloc(4096, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "go7007: unable to allocate 4096 bytes for " + "firmware construction\n"); + return -1; + } + memset(buf, 0, 4096); + + 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 = kmalloc(5120, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "go7007: unable to allocate 5120 bytes for " + "firmware construction\n"); + return -1; + } + memset(buf, 0, 5120); + 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 = kmalloc(5120, GFP_KERNEL); + if (buf == NULL) { + printk(KERN_ERR "go7007: unable to allocate 5120 bytes for " + "firmware construction\n"); + return -1; + } + memset(buf, 0, 5120); + 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 /* keep */ + /* 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 = kmalloc(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; + } + memset(code, 0, codespace * 2); + 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/linux/drivers/staging/go7007/go7007-i2c.c b/linux/drivers/staging/go7007/go7007-i2c.c new file mode 100644 index 000000000..b8cfa1a6e --- /dev/null +++ b/linux/drivers/staging/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 <linux/module.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/unistd.h> +#include <linux/time.h> +#include <linux/device.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <asm/system.h> + +#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/linux/drivers/staging/go7007/go7007-priv.h b/linux/drivers/staging/go7007/go7007-priv.h new file mode 100644 index 000000000..ce9307e3e --- /dev/null +++ b/linux/drivers/staging/go7007/go7007-priv.h @@ -0,0 +1,282 @@ +/* + * 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. + */ + +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; + 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; +}; + +/* 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/linux/drivers/staging/go7007/go7007-usb.c b/linux/drivers/staging/go7007/go7007-usb.c new file mode 100644 index 000000000..ff4fb36d9 --- /dev/null +++ b/linux/drivers/staging/go7007/go7007-usb.c @@ -0,0 +1,1287 @@ +/* + * 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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <asm/byteorder.h> +#include <media/tvaudio.h> + +#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_twTW2804", + .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_board", + .id = I2C_DRIVERID_S2250, + .addr = 0x43, + }, + }, + .num_inputs = 2, + .inputs = { + { + .video_input = 0, + .name = "Composite", + }, + { + .video_input = 1, + .name = "S-Video", + }, + }, + }, +}; + +static 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 = kmalloc(8, GFP_KERNEL); + if (tbuf == NULL) + return -ENOMEM; + memset(tbuf, 0, 8); + 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\n"; + 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); + + /* 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 = 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); + if (vurb->transfer_buffer) + kfree(vurb->transfer_buffer); + usb_free_urb(vurb); + } + aurb = usb->audio_urbs[i]; + if (aurb) { + usb_kill_urb(aurb); + if (aurb->transfer_buffer) + 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/linux/drivers/staging/go7007/go7007-v4l2.c b/linux/drivers/staging/go7007/go7007-v4l2.c new file mode 100644 index 000000000..c929204db --- /dev/null +++ b/linux/drivers/staging/go7007/go7007-v4l2.c @@ -0,0 +1,1876 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/version.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/spinlock.h> +#include <linux/fs.h> +#include <linux/unistd.h> +#include <linux/time.h> +#include <linux/vmalloc.h> +#include <linux/pagemap.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <asm/system.h> + +#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 + +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 video_decoder_resolution res; + + res.width = width; + if (height > sensor_height / 2) { + res.height = height / 2; + go->encoder_v_halve = 0; + } else { + res.height = height; + go->encoder_v_halve = 1; + } + if (go->i2c_adapter_online) + i2c_clients_command(&go->i2c_adapter, + DECODER_SET_RESOLUTION, &res); + } 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 /* keep */ +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; +} + +static int mpeg_queryctrl(u32 id, struct v4l2_queryctrl *ctrl) +{ + static const u32 user_ctrls[] = { + V4L2_CID_USER_CLASS, + 0 + }; + 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[] = { + user_ctrls, + mpeg_ctrls, + NULL + }; + + /* The ctrl may already contain the queried i2c controls, + * query the mpeg controls if the existing ctrl id is + * greater than the next mpeg ctrl id. + */ + id = v4l2_ctrl_next(ctrl_classes, id); + if (id >= ctrl->id && ctrl->name[0]) + return 0; + + memset(ctrl, 0, sizeof(*ctrl)); + ctrl->id = id; + + switch (ctrl->id) { + case V4L2_CID_USER_CLASS: + case V4L2_CID_MPEG_CLASS: + return v4l2_ctrl_query_fill_std(ctrl); + 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: + case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE: + return v4l2_ctrl_query_fill_std(ctrl); + case V4L2_CID_MPEG_VIDEO_BITRATE: + return v4l2_ctrl_query_fill(ctrl, + 64000, + 10000000, 1, + 9800000); + default: + break; + } + return -EINVAL; +} + +static int mpeg_s_control(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: + 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_control(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; +} +#endif + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + strlcpy(cap->driver, "go7007", sizeof(cap->driver)); + strlcpy(cap->card, go->name, sizeof(cap->card)); +#if 0 /* keep */ + 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_file *gofh = priv; + struct go7007 *go = gofh->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_file *gofh = priv; + struct go7007 *go = gofh->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_file *gofh = priv; + struct go7007 *go = gofh->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 = kmalloc(count * sizeof(struct go7007_buffer), + GFP_KERNEL); + + if (!gofh->bufs) { + mutex_unlock(&go->hw_lock); + goto unlock_and_return; + } + + memset(gofh->bufs, 0, count * sizeof(struct go7007_buffer)); + + 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_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (!go->i2c_adapter_online) + return -EIO; + + i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, query); + + return (!query->name[0]) ? -EINVAL : 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct v4l2_queryctrl query; + + if (!go->i2c_adapter_online) + return -EIO; + + memset(&query, 0, sizeof(query)); + query.id = ctrl->id; + i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); + if (query.name[0] == 0) + return -EINVAL; + i2c_clients_command(&go->i2c_adapter, VIDIOC_G_CTRL, ctrl); + + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + struct v4l2_queryctrl query; + + if (!go->i2c_adapter_online) + return -EIO; + + memset(&query, 0, sizeof(query)); + query.id = ctrl->id; + i2c_clients_command(&go->i2c_adapter, VIDIOC_QUERYCTRL, &query); + if (query.name[0] == 0) + return -EINVAL; + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_CTRL, ctrl); + + return 0; +} + +static int vidioc_g_parm(struct file *filp, void *priv, + struct v4l2_streamparm *parm) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->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_file *gofh = priv; + struct go7007 *go = gofh->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_file *gofh = priv; + struct go7007 *go = gofh->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_file *gofh = priv; + struct go7007 *go = gofh->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_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->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; + i2c_clients_command(&go->i2c_adapter, + VIDIOC_S_STD, std); + if (!*std) /* hack to indicate EINVAL from tuner */ + 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; + + if (go->i2c_adapter_online) + i2c_clients_command(&go->i2c_adapter, + VIDIOC_S_STD, std); + set_capture_size(go, NULL, 0); + + return 0; +} + +#if 0 /* keep */ + case VIDIOC_QUERYSTD: + { + v4l2_std_id *std = arg; + + if ((go->board_info->flags & GO7007_BOARD_HAS_TUNER) && + go->input == go->board_info->num_inputs - 1) { + if (!go->i2c_adapter_online) + return -EIO; + i2c_clients_command(&go->i2c_adapter, + VIDIOC_QUERYSTD, arg); + } 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; + } +#endif + +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->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_file *gofh = priv; + struct go7007 *go = gofh->go; + + *input = go->input; + + return 0; +} + +static int vidioc_s_input(struct file *file, void *priv, unsigned int input) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (input >= go->board_info->num_inputs) + return -EINVAL; + if (go->streaming) + return -EBUSY; + + go->input = input; + if (go->i2c_adapter_online) { + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_INPUT, + &go->board_info->inputs[input].video_input); + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_AUDIO, + &go->board_info->inputs[input].audio_input); + } + + return 0; +} + +static int vidioc_g_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->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; + + i2c_clients_command(&go->i2c_adapter, VIDIOC_G_TUNER, t); + + t->index = 0; + return 0; +} + +static int vidioc_s_tuner(struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->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; + } + + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_TUNER, t); + + return 0; +} + +static int vidioc_g_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->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; + i2c_clients_command(&go->i2c_adapter, VIDIOC_G_FREQUENCY, f); + return 0; +} + +static int vidioc_s_frequency(struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->go; + + if (!(go->board_info->flags & GO7007_BOARD_HAS_TUNER)) + return -EINVAL; + if (!go->i2c_adapter_online) + return -EIO; + + i2c_clients_command(&go->i2c_adapter, VIDIOC_S_FREQUENCY, f); + + return 0; +} + +static int vidioc_cropcap(struct file *file, void *priv, + struct v4l2_cropcap *cropcap) +{ + struct go7007_file *gofh = priv; + struct go7007 *go = gofh->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_file *gofh = priv; + struct go7007 *go = gofh->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 /* keep */ + /* 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_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .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, + .minor = -1, + .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; + memcpy(go->video_dev, &go7007_template, sizeof(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; + } + video_set_drvdata(go->video_dev, go); + ++go->ref_count; + printk(KERN_INFO "%s: registered device video%d [v4l2]\n", + go->video_dev->name, go->video_dev->num); + + 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); +} diff --git a/linux/drivers/staging/go7007/go7007.h b/linux/drivers/staging/go7007/go7007.h new file mode 100644 index 000000000..7399c915a --- /dev/null +++ b/linux/drivers/staging/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/linux/drivers/staging/go7007/go7007.txt b/linux/drivers/staging/go7007/go7007.txt new file mode 100644 index 000000000..06a76da32 --- /dev/null +++ b/linux/drivers/staging/go7007/go7007.txt @@ -0,0 +1,481 @@ +This is a driver for the WIS GO7007SB multi-format video encoder. + +Pete Eberlein <pete@sensoray.com> + +The driver was orignally 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/<KERNEL VERSION>/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 <http://linux-hotplug.sourceforge.net/>. +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/<KERNEL VERSION>/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/linux/drivers/staging/go7007/s2250-board.c b/linux/drivers/staging/go7007/s2250-board.c new file mode 100644 index 000000000..f35f0776c --- /dev/null +++ b/linux/drivers/staging/go7007/s2250-board.c @@ -0,0 +1,620 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include "s2250-loader.h" +#include "go7007-priv.h" +#include "wis-i2c.h" + +#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 { + int std; + int input; + int brightness; + int contrast; + int saturation; + int hue; + int reg12b_val; + int audio_input; + struct i2c_client *audio; +}; + +/* 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; + 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"); + return -EINTR; + } + if (go7007_usb_vendor_request(go, 0x57, addr, val, buf, 16, 1) < 0) + 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]; + 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 + return -EFAULT; + + /* save last 12b value */ + if (addr == 0x12b) + dec->reg12b_val = val; + + 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_command(struct i2c_client *client, + unsigned int cmd, void *arg) +{ + struct s2250 *dec = i2c_get_clientdata(client); + + switch (cmd) { + case VIDIOC_S_INPUT: + { + int vidsys; + int *input = arg; + + vidsys = (dec->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 { + /* S-Video */ + write_reg_fp(client, 0x20, 0x040 | vidsys); + write_reg_fp(client, 0x21, 0x666); + write_reg_fp(client, 0x140, 0x060); + } + dec->input = *input; + break; + } + case VIDIOC_S_STD: + { + v4l2_std_id *std = arg; + u16 vidsource; + + vidsource = (dec->input == 1) ? 0x040 : 0x020; + dec->std = *std; + switch (dec->std) { + 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; + } + break; + } + case VIDIOC_QUERYCTRL: + { + struct v4l2_queryctrl *ctrl = arg; + static const u32 user_ctrls[] = { + V4L2_CID_BRIGHTNESS, + V4L2_CID_CONTRAST, + V4L2_CID_SATURATION, + V4L2_CID_HUE, + 0 + }; + static const u32 *ctrl_classes[] = { + user_ctrls, + NULL + }; + + ctrl->id = v4l2_ctrl_next(ctrl_classes, ctrl->id); + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); + break; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); + break; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(ctrl, 0, 100, 1, 50); + break; + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(ctrl, -50, 50, 1, 0); + break; + default: + ctrl->name[0] = '\0'; + return -EINVAL; + } + break; + } + case VIDIOC_S_CTRL: + { + struct v4l2_control *ctrl = arg; + int value1; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + printk(KERN_INFO "s2250: future setting\n"); + return -EINVAL; + case V4L2_CID_CONTRAST: + printk(KERN_INFO "s2250: future setting\n"); + return -EINVAL; + 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; + + value1 = dec->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) + dec->hue = 50; + else if (ctrl->value < -50) + dec->hue = -50; + else + dec->hue = ctrl->value; + /* clamp the hue range */ + value1 = dec->hue * 280 / 50; + write_reg_fp(client, VPX322_ADDR_HUE, value1); + 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; + } + case VIDIOC_S_FMT: + { + struct v4l2_format *fmt = arg; + if (fmt->fmt.pix.height < 640) { + write_reg_fp(client, 0x12b, dec->reg12b_val | 0x400); + write_reg_fp(client, 0x140, 0x060); + } else { + write_reg_fp(client, 0x12b, dec->reg12b_val & ~0x400); + write_reg_fp(client, 0x140, 0x060); + } + return 0; + } + case VIDIOC_G_AUDIO: + { + struct v4l2_audio *audio = arg; + + memset(audio, 0, sizeof(*audio)); + audio->index = dec->audio_input; + /* fall through */ + } + case VIDIOC_ENUMAUDIO: + { + struct v4l2_audio *audio = arg; + + switch (audio->index) { + case 0: + strcpy(audio->name, "Line In"); + break; + case 1: + strcpy(audio->name, "Mic"); + break; + case 2: + strcpy(audio->name, "Mic Boost"); + break; + default: + audio->name[0] = '\0'; + return 0; + } + audio->capability = V4L2_AUDCAP_STEREO; + audio->mode = 0; + return 0; + } + case VIDIOC_S_AUDIO: + { + struct v4l2_audio *audio = arg; + + switch (audio->index) { + case 0: + write_reg(dec->audio, 0x08, 0x02); /* Line In */ + break; + case 1: + write_reg(dec->audio, 0x08, 0x04); /* Mic */ + break; + case 2: + write_reg(dec->audio, 0x08, 0x05); /* Mic Boost */ + break; + default: + return -EINVAL; + } + dec->audio_input = audio->index; + return 0; + } + + default: + printk(KERN_INFO "s2250: unknown command 0x%x\n", cmd); + break; + } + return 0; +} + +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 *dec; + 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; + + dec = kmalloc(sizeof(struct s2250), GFP_KERNEL); + if (dec == NULL) { + i2c_unregister_device(audio); + return -ENOMEM; + } + + dec->std = V4L2_STD_NTSC; + dec->brightness = 50; + dec->contrast = 50; + dec->saturation = 50; + dec->hue = 0; + dec->audio = audio; + i2c_set_clientdata(client, dec); + + printk(KERN_DEBUG + "s2250: initializing video decoder on %s\n", + adapter->name); + + /* initialize the audio */ + if (write_regs(audio, aud_regs) < 0) { + printk(KERN_ERR + "s2250: error initializing audio\n"); + i2c_unregister_device(audio); + kfree(dec); + return 0; + } + + if (write_regs(client, vid_regs) < 0) { + printk(KERN_ERR + "s2250: error initializing decoder\n"); + i2c_unregister_device(audio); + kfree(dec); + return 0; + } + if (write_regs_fp(client, vid_regs_fp) < 0) { + printk(KERN_ERR + "s2250: error initializing decoder\n"); + i2c_unregister_device(audio); + kfree(dec); + 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 */ + dec->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); + } + + printk("s2250: initialized successfully\n"); + return 0; +} + +static int s2250_remove(struct i2c_client *client) +{ + struct s2250 *dec = i2c_get_clientdata(client); + + i2c_set_clientdata(client, NULL); + i2c_unregister_device(dec->audio); + kfree(dec); + return 0; +} + +static struct i2c_device_id s2250_id[] = { + { "s2250_board", 0 }, + { } +}; + +static struct i2c_driver s2250_driver = { + .driver = { + .name = "Sensoray 2250 board driver", + }, + .probe = s2250_probe, + .remove = s2250_remove, + .command = s2250_command, + .id_table = s2250_id, +}; + +static int __init s2250_init(void) +{ + int r; + + r = s2250loader_init(); + if (r < 0) + return r; + + r = i2c_add_driver(&s2250_driver); + if (r < 0) + s2250loader_cleanup(); + + return r; +} + +static void __exit s2250_cleanup(void) +{ + i2c_del_driver(&s2250_driver); + + s2250loader_cleanup(); +} + +module_init(s2250_init); +module_exit(s2250_cleanup); + +MODULE_AUTHOR(""); +MODULE_DESCRIPTION("Board driver for Sensoryray 2250"); +MODULE_LICENSE("GPL v2"); diff --git a/linux/drivers/staging/go7007/s2250-loader.c b/linux/drivers/staging/go7007/s2250-loader.c new file mode 100644 index 000000000..d7bf82983 --- /dev/null +++ b/linux/drivers/staging/go7007/s2250-loader.c @@ -0,0 +1,189 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/smp_lock.h> +#include <linux/usb.h> +#include <dvb-usb.h> + +#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 = usb_get_intfdata(interface); + printk(KERN_INFO "s2250: disconnect\n"); + lock_kernel(); + s = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + kref_put(&(s->kref), s2250loader_delete); + unlock_kernel(); +} + +static 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, +}; + +int 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; +} +EXPORT_SYMBOL(s2250loader_init); + +void s2250loader_cleanup(void) +{ + printk(KERN_INFO "s2250loader_cleanup\n"); + usb_deregister(&s2250loader_driver); +} +EXPORT_SYMBOL(s2250loader_cleanup); diff --git a/linux/drivers/staging/go7007/s2250-loader.h b/linux/drivers/staging/go7007/s2250-loader.h new file mode 100644 index 000000000..b7c301af1 --- /dev/null +++ b/linux/drivers/staging/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/linux/drivers/staging/go7007/saa7134-go7007.c b/linux/drivers/staging/go7007/saa7134-go7007.c new file mode 100644 index 000000000..665bbf59d --- /dev/null +++ b/linux/drivers/staging/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 <linux/module.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/wait.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <asm/byteorder.h> +#include <media/v4l2-common.h> + +#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", + }, + }, +}; + +/********************* 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(&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(&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 = kmalloc(sizeof(struct saa7134_go7007), GFP_KERNEL); + if (saa == NULL) + return -ENOMEM; + memset(saa, 0, sizeof(struct saa7134_go7007)); + + /* 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/linux/drivers/staging/go7007/snd-go7007.c b/linux/drivers/staging/go7007/snd-go7007.c new file mode 100644 index 000000000..03c4dfc13 --- /dev/null +++ b/linux/drivers/staging/go7007/snd-go7007.c @@ -0,0 +1,305 @@ +/* + * 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 <linux/kernel.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/init.h> +#include <linux/spinlock.h> +#include <linux/delay.h> +#include <linux/sched.h> +#include <linux/vmalloc.h> +#include <linux/time.h> +#include <linux/mm.h> +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/uaccess.h> +#include <asm/system.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/initval.h> + +#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/linux/drivers/staging/go7007/wis-i2c.h b/linux/drivers/staging/go7007/wis-i2c.h new file mode 100644 index 000000000..3c2b9be45 --- /dev/null +++ b/linux/drivers/staging/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/linux/drivers/staging/go7007/wis-ov7640.c b/linux/drivers/staging/go7007/wis-ov7640.c new file mode 100644 index 000000000..04d6d3a49 --- /dev/null +++ b/linux/drivers/staging/go7007/wis-ov7640.c @@ -0,0 +1,107 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> + +#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 struct i2c_device_id wis_ov7640_id[] = { + { "wis_ov7640", 0 }, + { } +}; + +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/linux/drivers/staging/go7007/wis-saa7113.c b/linux/drivers/staging/go7007/wis-saa7113.c new file mode 100644 index 000000000..9ab893bd2 --- /dev/null +++ b/linux/drivers/staging/go7007/wis-saa7113.c @@ -0,0 +1,335 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> + +#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); + + i2c_set_clientdata(client, NULL); + kfree(dec); + return 0; +} + +static struct i2c_device_id wis_saa7113_id[] = { + { "wis_saa7113", 0 }, + { } +}; + +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/linux/drivers/staging/go7007/wis-saa7115.c b/linux/drivers/staging/go7007/wis-saa7115.c new file mode 100644 index 000000000..8687ad2de --- /dev/null +++ b/linux/drivers/staging/go7007/wis-saa7115.c @@ -0,0 +1,468 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> + +#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); + + i2c_set_clientdata(client, NULL); + kfree(dec); + return 0; +} + +static struct i2c_device_id wis_saa7115_id[] = { + { "wis_saa7115", 0 }, + { } +}; + +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/linux/drivers/staging/go7007/wis-sony-tuner.c b/linux/drivers/staging/go7007/wis-sony-tuner.c new file mode 100644 index 000000000..cfc1ec64e --- /dev/null +++ b/linux/drivers/staging/go7007/wis-sony-tuner.c @@ -0,0 +1,719 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/tuner.h> +#include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> + +#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 /* keep */ +#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); + + i2c_set_clientdata(client, NULL); + kfree(t); + return 0; +} + +static struct i2c_device_id wis_sony_tuner_id[] = { + { "wis_sony_tuner", 0 }, + { } +}; + +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/linux/drivers/staging/go7007/wis-tw2804.c b/linux/drivers/staging/go7007/wis-tw2804.c new file mode 100644 index 000000000..e15794a2a --- /dev/null +++ b/linux/drivers/staging/go7007/wis-tw2804.c @@ -0,0 +1,358 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> + +#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); + + i2c_set_clientdata(client, NULL); + kfree(dec); + return 0; +} + +static struct i2c_device_id wis_tw2804_id[] = { + { "wis_tw2804", 0 }, + { } +}; + +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/linux/drivers/staging/go7007/wis-tw9903.c b/linux/drivers/staging/go7007/wis-tw9903.c new file mode 100644 index 000000000..1360dd1bd --- /dev/null +++ b/linux/drivers/staging/go7007/wis-tw9903.c @@ -0,0 +1,340 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <linux/ioctl.h> + +#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 /* keep */ + /* 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 /* keep */ + /* 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 /* keep */ + 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 /* keep */ + 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); + + i2c_set_clientdata(client, NULL); + kfree(dec); + return 0; +} + +static struct i2c_device_id wis_tw9903_id[] = { + { "wis_tw9903", 0 }, + { } +}; + +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/linux/drivers/staging/go7007/wis-uda1342.c b/linux/drivers/staging/go7007/wis-uda1342.c new file mode 100644 index 000000000..739c7ae89 --- /dev/null +++ b/linux/drivers/staging/go7007/wis-uda1342.c @@ -0,0 +1,113 @@ +/* + * 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 <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/videodev2.h> +#include <media/tvaudio.h> +#include <media/v4l2-common.h> + +#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 struct i2c_device_id wis_uda1342_id[] = { + { "wis_uda1342", 0 }, + { } +}; + +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/linux/drivers/staging/tm6000/Kconfig b/linux/drivers/staging/tm6000/Kconfig new file mode 100644 index 000000000..cb2011536 --- /dev/null +++ b/linux/drivers/staging/tm6000/Kconfig @@ -0,0 +1,32 @@ +config VIDEO_TM6000 + tristate "TV Master TM5600/6000/6010 driver" + depends on VIDEO_DEV && I2C && INPUT && EXPERIMENTAL + select VIDEO_TUNER + select TUNER_XC2028 + select VIDEOBUF_VMALLOC + help + Support for TM5600/TM6000/TM6010 USB Device + + Since these cards have no MPEG decoder onboard, they transmit + only compressed MPEG data over the usb bus, so you need + an external software decoder to watch TV on your computer. + + Say Y if you own such a device and want to use it. + +config VIDEO_TM6000_ALSA + tristate "TV Master TM5600/6000/6010 audio support" + depends on VIDEO_TM6000 && SND && EXPERIMENTAL + select SND_PCM + ---help--- + This is a video4linux driver for direct (DMA) audio for + TM5600/TM6000/TM6010 USB Devices. + + To compile this driver as a module, choose M here: the + module will be called tm6000-alsa. + +config VIDEO_TM6000_DVB + bool "DVB Support for tm6000 based TV cards" + depends on VIDEO_TM6000 && DVB_CORE && EXPERIMENTAL + select DVB_ZL10353 + ---help--- + This adds support for DVB cards based on the tm5600/tm6000 chip. diff --git a/linux/drivers/staging/tm6000/Makefile b/linux/drivers/staging/tm6000/Makefile new file mode 100644 index 000000000..25aefe74d --- /dev/null +++ b/linux/drivers/staging/tm6000/Makefile @@ -0,0 +1,15 @@ +tm6000-objs := tm6000-cards.o \ + tm6000-core.o \ + tm6000-i2c.o \ + tm6000-video.o \ + tm6000-stds.o + +ifeq ($(CONFIG_VIDEO_TM6000_DVB),y) +tm6000-objs += tm6000-dvb.o \ + hack.o +endif + +obj-$(CONFIG_VIDEO_TM6000) += tm6000.o +obj-$(CONFIG_VIDEO_TM6000_ALSA) += tm6000-alsa.o + +EXTRA_CFLAGS = -Idrivers/media/video diff --git a/linux/drivers/staging/tm6000/README b/linux/drivers/staging/tm6000/README new file mode 100644 index 000000000..cff09b7b4 --- /dev/null +++ b/linux/drivers/staging/tm6000/README @@ -0,0 +1,11 @@ +Todo: + - checkpatch.pl cleanups + - sparse cleanups + - convert to new i2c approach + - better support DVB + - fix reading from i2c, if possible + - fix loosing frames + - fix oops? + +Please send patches to linux-media@vger.kernel.org + diff --git a/linux/drivers/staging/tm6000/hack.c b/linux/drivers/staging/tm6000/hack.c new file mode 100644 index 000000000..f181fce67 --- /dev/null +++ b/linux/drivers/staging/tm6000/hack.c @@ -0,0 +1,252 @@ + + + + + + +/* + hack.h - hackish code that needs to be improved (or removed) at a + later point + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "hack.h" + +#include "tm6000.h" + +#include <linux/usb.h> + +static inline int tm6000_snd_control_msg(struct tm6000_core *dev, __u8 request, __u16 value, __u16 index, void *data, __u16 size) +{ + return tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, request, value, index, data, size); +} + +static int pseudo_zl10353_pll(struct tm6000_core *tm6000_dev, struct dvb_frontend_parameters *p) +{ + int ret; + u8 *data = kzalloc(50*sizeof(u8), GFP_KERNEL); + +printk(KERN_ALERT "should set frequency %u\n", p->frequency); +printk(KERN_ALERT "and bandwith %u\n", p->u.ofdm.bandwidth); + + if(tm6000_dev->dvb->frontend->ops.tuner_ops.set_params) { + tm6000_dev->dvb->frontend->ops.tuner_ops.set_params(tm6000_dev->dvb->frontend, p); + } + else { + printk(KERN_ALERT "pseudo zl10353: couldn't set tuner parameters\n"); + } + + // init ZL10353 + data[0] = 0x0b; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x501e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x80; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x551e, 0x00, data, 0x1); + msleep(100); + data[0] = 0x01; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0xea1e, 0x00, data, 0x1); + msleep(100); + data[0] = 0x00; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0xea1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x1c; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x561e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x40; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5e1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x36; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x641e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x67; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x651e, 0x00, data, 0x1); + msleep(15); + data[0] = 0xe5; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x661e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x19; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6c1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0xe9; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6d1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x44; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x511e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x46; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x521e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x15; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x531e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x0f; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x541e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x75; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5c1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x01; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x00; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1); + msleep(15); + + msleep(50); + + switch(p->u.ofdm.bandwidth) { + case BANDWIDTH_8_MHZ: + data[0] = 0x00; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x36; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x641e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x67; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x651e, 0x00, data, 0x1); + msleep(15); + data[0] = 0xe5; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x661e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x19; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6c1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0xe9; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6d1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x44; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x511e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x46; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x521e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x15; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x531e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x0f; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x541e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x75; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5c1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x01; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1); + msleep(15); + break; + + default: + printk(KERN_ALERT "tm6000: bandwidth not supported\n"); + case BANDWIDTH_7_MHZ: + data[0] = 0x00; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x35; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x641e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x5a; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x651e, 0x00, data, 0x1); + msleep(15); + data[0] = 0xe9; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x661e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x19; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6c1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0xe9; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x6d1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x44; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x511e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x46; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x521e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x15; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x531e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x0f; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x541e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x86; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x5c1e, 0x00, data, 0x1); + msleep(15); + data[0] = 0x01; + ret = tm6000_snd_control_msg(tm6000_dev, 0x10, 0x701e, 0x00, data, 0x1); + msleep(15); + break; + } + + kfree(data); + + return 0; +}; + + + +int pseudo_zl10353_set_frontend(struct dvb_frontend *fe, + struct dvb_frontend_parameters *p) +{ + struct tm6000_core *tm6000_dev = fe->dvb->priv; + u32 status; + + if(p != NULL) { +// mutex_lock(&tm6000_dev->mutex); + pseudo_zl10353_pll(tm6000_dev, p); +// mutex_unlock(&tm6000_dev->mutex); + } + + if(tm6000_dev->dvb->frontend->ops.read_status) { + tm6000_dev->dvb->frontend->ops.read_status(tm6000_dev->dvb->frontend, &status); + printk(KERN_ALERT "demodulator status: FE_HAS_CARRIER %i \n", (status & FE_HAS_CARRIER)); + printk(KERN_ALERT "demodulator status: FE_HAS_VITERBI %i \n", (status & FE_HAS_VITERBI)); + printk(KERN_ALERT "demodulator status: FE_HAS_LOCK %i \n", (status & FE_HAS_LOCK)); + printk(KERN_ALERT "demodulator status: FE_HAS_SYNC %i \n", (status & FE_HAS_SYNC)); + printk(KERN_ALERT "demodulator status: FE_HAS_SIGNAL %i \n", (status & FE_HAS_SIGNAL)); + } + else { + printk(KERN_ALERT "pseudo zl10353: couldn't read demodulator status\n"); + } + return 0; +} + +int pseudo_zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status) +{ + + *status = FE_HAS_CARRIER | FE_HAS_VITERBI | FE_HAS_SYNC | FE_HAS_LOCK | FE_HAS_SIGNAL; + + return 0; +} + +struct dvb_frontend* pseudo_zl10353_attach(struct tm6000_core *dev, + const struct zl10353_config *config, + struct i2c_adapter *i2c) +{ + struct tm6000_dvb *dvb = dev->dvb; + + dvb->frontend = dvb_attach(zl10353_attach, config, i2c); + if(!dvb->frontend) { + printk(KERN_ERR "Error during zl10353_attach!\n"); + return NULL; + } + + /* override some functions with our implementations */ + dvb->frontend->ops.set_frontend = pseudo_zl10353_set_frontend; + dvb->frontend->ops.read_status = pseudo_zl10353_read_status; + dvb->frontend->frontend_priv = dev; + + return dvb->frontend; +} diff --git a/linux/drivers/staging/tm6000/hack.h b/linux/drivers/staging/tm6000/hack.h new file mode 100644 index 000000000..96f1b61df --- /dev/null +++ b/linux/drivers/staging/tm6000/hack.h @@ -0,0 +1,45 @@ +/* + hack.h - hackish code that needs to be improved (or removed) at a + later point + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef HACK_H +#define HACK_H + +#include <linux/i2c.h> + +#include "zl10353.h" +#include "dvb_frontend.h" + +struct tm6000_core; + +int pseudo_zl103530_init(struct dvb_frontend *fe); + +int pseudo_zl10353_set_frontend(struct dvb_frontend *fe, struct dvb_frontend_parameters *p); + +int pseudo_zl10353_read_status(struct dvb_frontend *fe, fe_status_t *status); + +int pseudo_zl10353_read_signal_strength(struct dvb_frontend* fe, u16* strength); + +int pseudo_zl10353_read_snr(struct dvb_frontend *fe, u16 *snr); + +struct dvb_frontend* pseudo_zl10353_attach(struct tm6000_core *dev, + const struct zl10353_config *config, + struct i2c_adapter *i2c); + +#endif diff --git a/linux/drivers/staging/tm6000/tm6000-alsa.c b/linux/drivers/staging/tm6000/tm6000-alsa.c new file mode 100644 index 000000000..a5654e1c5 --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-alsa.c @@ -0,0 +1,441 @@ +/* + * + * Support for audio capture for tm5600/6000 + * (c) 2007-2008 Mauro Carvalho Chehab <mchehab@redhat.com> + * + * Based on cx88-alsa.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/usb.h> + +#include <asm/delay.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/control.h> +#include <sound/initval.h> + +#include "compat.h" + +#include "tm6000.h" +#include "tm6000-regs.h" + +#undef dprintk + +#define dprintk(level, fmt, arg...) do { \ + if (debug >= level) \ + printk(KERN_INFO "%s/1: " fmt, chip->core->name , ## arg); \ + } while (0) + +/**************************************************************************** + Data type declarations - Can be moded to a header file later + ****************************************************************************/ + +struct snd_tm6000_card { + struct snd_card *card; + + spinlock_t reg_lock; + + atomic_t count; + + unsigned int period_size; + unsigned int num_periods; + + struct tm6000_core *core; + struct tm6000_buffer *buf; + + int bufsize; + + struct snd_pcm_substream *substream; +}; + +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 8) +#define chip_t snd_tm6000_card_t +#endif + +/**************************************************************************** + Module global static vars + ****************************************************************************/ + +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; /* Index 0-MAX */ +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; /* ID for this card */ +static int enable[SNDRV_CARDS] = {1, [1 ... (SNDRV_CARDS - 1)] = 1}; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) +static unsigned int dummy; +module_param_array(enable, bool, dummy, 0444); +#else +module_param_array(enable, bool, NULL, 0444); +#endif +MODULE_PARM_DESC(enable, "Enable tm6000x soundcard. default enabled."); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 10) +module_param_array(index, int, dummy, 0444); +#else +module_param_array(index, int, NULL, 0444); +#endif +MODULE_PARM_DESC(index, "Index value for tm6000x capture interface(s)."); + + +/**************************************************************************** + Module macros + ****************************************************************************/ + +MODULE_DESCRIPTION("ALSA driver module for tm5600/tm6000 based TV cards"); +MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@redhat.com>"); +MODULE_LICENSE("GPL"); +MODULE_SUPPORTED_DEVICE("{{Trident,tm5600}," + "{{Trident,tm6000}"); +static unsigned int debug; +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "enable debug messages"); + +/**************************************************************************** + Module specific funtions + ****************************************************************************/ + +/* + * BOARD Specific: Sets audio DMA + */ + +static int _tm6000_start_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + int val; + + /* Enables audio */ + val = tm6000_get_reg(core, REQ_07_SET_GET_AVREG, 0xcc, 0x0); + val |= 0x20; + tm6000_set_reg(core, REQ_07_SET_GET_AVREG, 0xcc, val); + + tm6000_set_reg(core, REQ_08_SET_GET_AVREG_BIT, 0x01, 0x80); + + return 0; +} + +/* + * BOARD Specific: Resets audio DMA + */ +static int _tm6000_stop_audio_dma(struct snd_tm6000_card *chip) +{ + struct tm6000_core *core = chip->core; + int val; + dprintk(1, "Stopping audio DMA\n"); + + /* Enables audio */ + val = tm6000_get_reg(core, REQ_07_SET_GET_AVREG, 0xcc, 0x0); + val &= ~0x20; + tm6000_set_reg(core, REQ_07_SET_GET_AVREG, 0xcc, val); + + tm6000_set_reg(core, REQ_08_SET_GET_AVREG_BIT, 0x01, 0); + + return 0; +} + +static int dsp_buffer_free(struct snd_tm6000_card *chip) +{ + BUG_ON(!chip->bufsize); + + dprintk(2, "Freeing buffer\n"); + + /* FIXME: Frees buffer */ + + chip->bufsize = 0; + + return 0; +} + +/**************************************************************************** + ALSA PCM Interface + ****************************************************************************/ + +/* + * Digital hardware definition + */ +#define DEFAULT_FIFO_SIZE 4096 + +static struct snd_pcm_hardware snd_tm6000_digital_hw = { + .info = SNDRV_PCM_INFO_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_44100 | SNDRV_PCM_RATE_48000, + .rate_min = 44100, + .rate_max = 48000, + .channels_min = 2, + .channels_max = 2, + .period_bytes_min = DEFAULT_FIFO_SIZE/4, + .period_bytes_max = DEFAULT_FIFO_SIZE/4, + .periods_min = 1, + .periods_max = 1024, + .buffer_bytes_max = (1024*1024), +}; + +/* + * audio pcm capture open callback + */ +static int snd_tm6000_pcm_open(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + int err; + + err = snd_pcm_hw_constraint_pow2(runtime, 0, + SNDRV_PCM_HW_PARAM_PERIODS); + if (err < 0) + goto _error; + + chip->substream = substream; + + runtime->hw = snd_tm6000_digital_hw; + + return 0; +_error: + dprintk(1, "Error opening PCM!\n"); + return err; +} + +/* + * audio close callback + */ +static int snd_tm6000_close(struct snd_pcm_substream *substream) +{ + return 0; +} + +/* + * hw_params callback + */ +static int snd_tm6000_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *hw_params) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + chip->period_size = params_period_bytes(hw_params); + chip->num_periods = params_periods(hw_params); + chip->bufsize = chip->period_size * params_periods(hw_params); + + BUG_ON(!chip->bufsize); + + dprintk(1, "Setting buffer\n"); + + /* FIXME: Allocate buffer for audio */ + +#if 0 + substream->runtime->dma_area = chip->dma_risc->vmalloc; +#endif + + return 0; +} + +/* + * hw free callback + */ +static int snd_tm6000_hw_free(struct snd_pcm_substream *substream) +{ + + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + + if (substream->runtime->dma_area) { + dsp_buffer_free(chip); + substream->runtime->dma_area = NULL; + } + + return 0; +} + +/* + * prepare callback + */ +static int snd_tm6000_prepare(struct snd_pcm_substream *substream) +{ + return 0; +} + + +/* + * trigger callback + */ +static int snd_tm6000_card_trigger(struct snd_pcm_substream *substream, int cmd) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + int err; + + spin_lock(&chip->reg_lock); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = _tm6000_start_audio_dma(chip); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = _tm6000_stop_audio_dma(chip); + break; + default: + err = -EINVAL; + break; + } + + spin_unlock(&chip->reg_lock); + + return err; +} + +/* + * pointer callback + */ +static snd_pcm_uframes_t snd_tm6000_pointer(struct snd_pcm_substream *substream) +{ + struct snd_tm6000_card *chip = snd_pcm_substream_chip(substream); + struct snd_pcm_runtime *runtime = substream->runtime; + u16 count; + + count = atomic_read(&chip->count); + + return runtime->period_size * (count & (runtime->periods-1)); +} + +/* + * operators + */ +static struct snd_pcm_ops snd_tm6000_pcm_ops = { + .open = snd_tm6000_pcm_open, + .close = snd_tm6000_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_tm6000_hw_params, + .hw_free = snd_tm6000_hw_free, + .prepare = snd_tm6000_prepare, + .trigger = snd_tm6000_card_trigger, + .pointer = snd_tm6000_pointer, +}; + +/* + * create a PCM device + */ +static int __devinit snd_tm6000_pcm(struct snd_tm6000_card *chip, + int device, char *name) +{ + int err; + struct snd_pcm *pcm; + + err = snd_pcm_new(chip->card, name, device, 0, 1, &pcm); + if (err < 0) + return err; + pcm->private_data = chip; + strcpy(pcm->name, name); + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_tm6000_pcm_ops); + + return 0; +} + +/* FIXME: Control interface - How to control volume/mute? */ + +/**************************************************************************** + Basic Flow for Sound Devices + ****************************************************************************/ + +/* + * Alsa Constructor - Component probe + */ + +int tm6000_audio_init(struct tm6000_core *dev, int idx) +{ + struct snd_card *card; + struct snd_tm6000_card *chip; + int rc, len; + char component[14]; + + if (idx >= SNDRV_CARDS) + return -ENODEV; + + if (!enable[idx]) + return -ENOENT; + + rc = snd_card_create(index[idx], id[idx], THIS_MODULE, 0, &card); + if (rc < 0) { + snd_printk(KERN_ERR "cannot create card instance %d\n", idx); + return rc; + } + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) { + rc = -ENOMEM; + goto error; + } + + chip->core = dev; + chip->card = card; + + strcpy(card->driver, "tm6000-alsa"); + sprintf(component, "USB%04x:%04x", + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct)); + snd_component_add(card, component); + + if (dev->udev->descriptor.iManufacturer) + len = usb_string(dev->udev, + dev->udev->descriptor.iManufacturer, + card->longname, sizeof(card->longname)); + else + len = 0; + + if (len > 0) + strlcat(card->longname, " ", sizeof(card->longname)); + + strlcat(card->longname, card->shortname, sizeof(card->longname)); + + len = strlcat(card->longname, " at ", sizeof(card->longname)); + + if (len < sizeof(card->longname)) + usb_make_path(dev->udev, card->longname + len, + sizeof(card->longname) - len); + + strlcat(card->longname, + dev->udev->speed == USB_SPEED_LOW ? ", low speed" : + dev->udev->speed == USB_SPEED_FULL ? ", full speed" : + ", high speed", + sizeof(card->longname)); + + rc = snd_tm6000_pcm(chip, 0, "tm6000 Digital"); + if (rc < 0) + goto error; + + rc = snd_card_register(card); + if (rc < 0) + goto error; + + + return 0; + +error: + snd_card_free(card); + return rc; +} +EXPORT_SYMBOL_GPL(tm6000_audio_init); + +#if 0 +/* + * module remove + */ +static void tm6000_audio_fini(void) +{ +} + +module_init(tm6000_audio_init); +module_exit(tm6000_audio_fini); + +#endif diff --git a/linux/drivers/staging/tm6000/tm6000-cards.c b/linux/drivers/staging/tm6000/tm6000-cards.c new file mode 100644 index 000000000..7b52b403a --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-cards.c @@ -0,0 +1,676 @@ +/* + tm6000-cards.c - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/init.h> +#include <linux/module.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/usb.h> +#include <linux/version.h> +#include "compat.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include <media/tvaudio.h> +#include <media/i2c-addr.h> + +#include "tm6000.h" +#include "tm6000-regs.h" +#include "tuner-xc2028.h" + +#define TM6000_BOARD_UNKNOWN 0 +#define TM5600_BOARD_GENERIC 1 +#define TM6000_BOARD_GENERIC 2 +#define TM6010_BOARD_GENERIC 3 +#define TM5600_BOARD_10MOONS_UT821 4 +#define TM5600_BOARD_10MOONS_UT330 5 +#define TM6000_BOARD_ADSTECH_DUAL_TV 6 +#define TM6000_BOARD_FREECOM_AND_SIMILAR 7 +#define TM6000_BOARD_ADSTECH_MINI_DUAL_TV 8 +#define TM6010_BOARD_HAUPPAUGE_900H 9 + +#define TM6000_MAXBOARDS 16 +static unsigned int card[] = {[0 ... (TM6000_MAXBOARDS - 1)] = UNSET }; + +module_param_array(card, int, NULL, 0444); + +static unsigned long tm6000_devused; + +#if 0 +/* This will be needed sooner or later, to allow selecting video mux entries */ +#define MAX_TM6000_INPUT 3 + +struct tm6000_input { + enum tm6000_itype type; + u32 gpio_addr; + unsigned int gpio_val; + int vmux; /* Used to control registers 0xe3, 0xe8, 0xeb */ +}; +#endif + +struct tm6000_board { + char *name; + + struct tm6000_capabilities caps; + + enum tm6000_devtype type; /* variant of the chipset */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int demod_addr; /* demodulator address */ + int gpio_addr_tun_reset; /* GPIO used for tuner reset */ +#if 0 + struct tm6000_input input[MAX_TM6000_INPUT]; +#endif +}; + +struct tm6000_board tm6000_boards[] = { + [TM6000_BOARD_UNKNOWN] = { + .name = "Unknown tm6000 video grabber", + .caps = { + .has_tuner = 1, + }, + .gpio_addr_tun_reset = TM6000_GPIO_1, + }, + [TM5600_BOARD_GENERIC] = { + .name = "Generic tm5600 board", + .type = TM5600, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + }, + .gpio_addr_tun_reset = TM6000_GPIO_1, + }, + [TM6000_BOARD_GENERIC] = { + .name = "Generic tm6000 board", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + }, + .gpio_addr_tun_reset = TM6000_GPIO_1, + }, + [TM6010_BOARD_GENERIC] = { + .name = "Generic tm6010 board", + .type = TM6010, + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + }, + .gpio_addr_tun_reset = TM6010_GPIO_4, + }, + [TM5600_BOARD_10MOONS_UT821] = { + .name = "10Moons UT 821", + .tuner_type = TUNER_XC2028, + .type = TM5600, + .tuner_addr = 0xc2 >> 1, + .caps = { + .has_tuner = 1, + .has_eeprom = 1, + }, + .gpio_addr_tun_reset = TM6000_GPIO_1, + }, + [TM5600_BOARD_10MOONS_UT330] = { + .name = "10Moons UT 330", + .tuner_type = TUNER_PHILIPS_FQ1216AME_MK4, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 0, + .has_zl10353 = 0, + .has_eeprom = 1, + }, + }, + [TM6000_BOARD_ADSTECH_DUAL_TV] = { + .name = "ADSTECH Dual TV USB", + .tuner_type = TUNER_XC2028, + .tuner_addr = 0xc8 >> 1, + .caps = { + .has_tuner = 1, + .has_tda9874 = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + }, + [TM6000_BOARD_FREECOM_AND_SIMILAR] = { + .name = "Freecom Hybrid Stick / Moka DVB-T Receiver Dual", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + .has_remote = 1, + }, + .gpio_addr_tun_reset = TM6000_GPIO_4, + }, + [TM6000_BOARD_ADSTECH_MINI_DUAL_TV] = { + .name = "ADSTECH Mini Dual TV USB", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc8 >> 1, + .demod_addr = 0x1e >> 1, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 0, + }, + .gpio_addr_tun_reset = TM6000_GPIO_4, + }, + [TM6010_BOARD_HAUPPAUGE_900H] = { + .name = "Hauppauge HVR-900H", + .tuner_type = TUNER_XC2028, /* has a XC3028 */ + .tuner_addr = 0xc2 >> 1, + .demod_addr = 0x1e >> 1, + .type = TM6010, + .caps = { + .has_tuner = 1, + .has_dvb = 1, + .has_zl10353 = 1, + .has_eeprom = 1, + }, + .gpio_addr_tun_reset = TM6000_GPIO_2, + }, +}; + +/* table of devices that work with this driver */ +struct usb_device_id tm6000_id_table [] = { + { USB_DEVICE(0x6000, 0x0001), .driver_info = TM5600_BOARD_10MOONS_UT821 }, + { USB_DEVICE(0x6000, 0x0002), .driver_info = TM6010_BOARD_GENERIC }, + { USB_DEVICE(0x06e1, 0xf332), .driver_info = TM6000_BOARD_ADSTECH_DUAL_TV }, + { USB_DEVICE(0x14aa, 0x0620), .driver_info = TM6000_BOARD_FREECOM_AND_SIMILAR }, + { USB_DEVICE(0x06e1, 0xb339), .driver_info = TM6000_BOARD_ADSTECH_MINI_DUAL_TV }, + { USB_DEVICE(0x2040, 0x6600), .driver_info = TM6010_BOARD_HAUPPAUGE_900H }, + { }, +}; + +/* Tuner callback to provide the proper gpio changes needed for xc2028 */ + +static int tm6000_tuner_callback(void *ptr, int component, int command, int arg) +{ + int rc=0; + struct tm6000_core *dev = ptr; + + if (dev->tuner_type!=TUNER_XC2028) + return 0; + + switch (command) { + case XC2028_RESET_CLK: + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, arg); + msleep(10); + rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 0); + if (rc<0) + return rc; + msleep(10); + rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 1); + break; + case XC2028_TUNER_RESET: + /* Reset codes during load firmware */ + switch (arg) { + case 0: + tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, + dev->tuner_reset_gpio, 0x00); + msleep(130); + tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, + dev->tuner_reset_gpio, 0x01); + msleep(130); + break; + case 1: + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, + 0x02, 0x01); + msleep(10); + break; + + case 2: + rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 0); + if (rc<0) + return rc; + msleep(100); + rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, + TM6000_GPIO_CLK, 1); + msleep(100); + break; + } + } + return (rc); +} + +static void tm6000_config_tuner (struct tm6000_core *dev) +{ + struct tuner_setup tun_setup; + + /* Load tuner module */ + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tuner", "tuner",dev->tuner_addr, NULL); + + memset(&tun_setup, 0, sizeof(tun_setup)); + tun_setup.type = dev->tuner_type; + tun_setup.addr = dev->tuner_addr; + tun_setup.mode_mask = T_ANALOG_TV | T_RADIO; + tun_setup.tuner_callback = tm6000_tuner_callback; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr, &tun_setup); + + if (dev->tuner_type == TUNER_XC2028) { + struct v4l2_priv_tun_config xc2028_cfg; + struct xc2028_ctrl ctl; + + memset(&xc2028_cfg, 0, sizeof(xc2028_cfg)); + memset (&ctl,0,sizeof(ctl)); + + ctl.mts = 1; + ctl.read_not_reliable = 1; + ctl.msleep = 10; + + xc2028_cfg.tuner = TUNER_XC2028; + xc2028_cfg.priv = &ctl; + + switch(dev->model) { + case TM6010_BOARD_HAUPPAUGE_900H: + ctl.fname = "xc3028L-v36.fw"; + break; + default: + if (dev->dev_type == TM6010) + ctl.fname = "xc3028-v27.fw"; + else + ctl.fname = "tm6000-xc3028.fw"; + } + + printk(KERN_INFO "Setting firmware parameters for xc2028\n"); + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_config, + &xc2028_cfg); + } +} + +static int tm6000_init_dev(struct tm6000_core *dev) +{ + struct v4l2_frequency f; + int rc = 0; + + mutex_init(&dev->lock); + + mutex_lock(&dev->lock); + + /* Initializa board-specific data */ + dev->dev_type = tm6000_boards[dev->model].type; + dev->tuner_type = tm6000_boards[dev->model].tuner_type; + dev->tuner_addr = tm6000_boards[dev->model].tuner_addr; + dev->tuner_reset_gpio = tm6000_boards[dev->model].gpio_addr_tun_reset; + + dev->demod_addr = tm6000_boards[dev->model].demod_addr; + + dev->caps = tm6000_boards[dev->model].caps; + + /* initialize hardware */ + rc=tm6000_init (dev); + if (rc<0) + goto err; + + rc = v4l2_device_register(&dev->udev->dev, &dev->v4l2_dev); + if (rc < 0) + goto err; + + /* register i2c bus */ + rc=tm6000_i2c_register(dev); + if (rc<0) + goto err; + + /* register and initialize V4L2 */ + rc=tm6000_v4l2_register(dev); + if (rc<0) + goto err; + + /* Default values for STD and resolutions */ + dev->width = 720; + dev->height = 480; + dev->norm = V4L2_STD_PAL_M; + + /* Configure tuner */ + tm6000_config_tuner (dev); + + /* Set video standard */ + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + /* Set tuner frequency - also loads firmware on xc2028/xc3028 */ + f.tuner = 0; + f.type = V4L2_TUNER_ANALOG_TV; + f.frequency = 3092; /* 193.25 MHz */ + dev->freq = f.frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + + if (dev->caps.has_tda9874) + v4l2_i2c_new_subdev(&dev->v4l2_dev, &dev->i2c_adap, + "tvaudio", "tvaudio", I2C_ADDR_TDA9874, NULL); + + if(dev->caps.has_dvb) { + dev->dvb = kzalloc(sizeof(*(dev->dvb)), GFP_KERNEL); + if(!dev->dvb) { + rc = -ENOMEM; + goto err2; + } +#ifdef CONFIG_VIDEO_TM6000_DVB + rc = tm6000_dvb_register(dev); + if(rc < 0) { + kfree(dev->dvb); + dev->dvb = NULL; + goto err2; + } +#endif + } + +err2: + v4l2_device_unregister(&dev->v4l2_dev); + +err: + mutex_unlock(&dev->lock); + return rc; +} + +/* high bandwidth multiplier, as encoded in highspeed endpoint descriptors */ +#define hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) + +static void get_max_endpoint ( struct usb_device *usbdev, + char *msgtype, + struct usb_host_endpoint *curr_e, + unsigned int *maxsize, + struct usb_host_endpoint **ep ) +{ + u16 tmp = le16_to_cpu(curr_e->desc.wMaxPacketSize); + unsigned int size = tmp & 0x7ff; + + if (usbdev->speed == USB_SPEED_HIGH) + size = size * hb_mult (tmp); + + if (size>*maxsize) { + *ep = curr_e; + *maxsize = size; + printk("tm6000: %s endpoint: 0x%02x (max size=%u bytes)\n", + msgtype, curr_e->desc.bEndpointAddress, + size); + } +} + +/* + * tm6000_usb_probe() + * checks for supported devices + */ +static int tm6000_usb_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct usb_device *usbdev; + struct tm6000_core *dev = NULL; + int i,rc=0; + int nr=0; + char *speed; + +#if 0 + if(interface->minor != 0) + return -1; +#endif + + usbdev=usb_get_dev(interface_to_usbdev(interface)); + + /* Selects the proper interface */ + rc=usb_set_interface(usbdev,0,1); + if (rc<0) + goto err; + + /* Check to see next free device and mark as used */ + nr=find_first_zero_bit(&tm6000_devused,TM6000_MAXBOARDS); + if (nr >= TM6000_MAXBOARDS) { + printk ("tm6000: Supports only %i em28xx boards.\n",TM6000_MAXBOARDS); + usb_put_dev(usbdev); + return -ENOMEM; + } + + /* Create and initialize dev struct */ + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (dev == NULL) { + printk ("tm6000" ": out of memory!\n"); + usb_put_dev(usbdev); + return -ENOMEM; + } + spin_lock_init(&dev->slock); + + /* Increment usage count */ + tm6000_devused|=1<<nr; + snprintf(dev->name, 29, "tm6000 #%d", nr); + + dev->model=id->driver_info; + if ((card[nr]>=0) && (card[nr]<ARRAY_SIZE(tm6000_boards))) { + dev->model=card[nr]; + } + + INIT_LIST_HEAD(&dev->tm6000_corelist); + dev->udev= usbdev; + dev->devno=nr; + + switch (usbdev->speed) { + case USB_SPEED_LOW: + speed = "1.5"; + break; + case USB_SPEED_UNKNOWN: + case USB_SPEED_FULL: + speed = "12"; + break; + case USB_SPEED_HIGH: + speed = "480"; + break; + default: + speed = "unknown"; + } + + +#if 0 + if (usb_reset_configuration (dev->udev) < 0) { + err("reset_configuration failed"); + goto err; + } +#endif + + /* Get endpoints */ + for (i = 0; i < interface->num_altsetting; i++) { + int ep; + + for (ep = 0; ep < interface->altsetting[i].desc.bNumEndpoints; ep++) { + struct usb_host_endpoint *e; + int dir_out; + + e = &interface->altsetting[i].endpoint[ep]; + + dir_out = ((e->desc.bEndpointAddress & + USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT); + + printk("tm6000: alt %d, interface %i, class %i\n", + i, + interface->altsetting[i].desc.bInterfaceNumber, + interface->altsetting[i].desc.bInterfaceClass); + + switch (e->desc.bmAttributes) { + case USB_ENDPOINT_XFER_BULK: + if (!dir_out) { + get_max_endpoint (usbdev, "Bulk IN", e, + &dev->max_bulk_in, + &dev->bulk_in); + } else { + get_max_endpoint (usbdev, "Bulk OUT", e, + &dev->max_bulk_out, + &dev->bulk_out); + } + break; + case USB_ENDPOINT_XFER_ISOC: + if (!dir_out) { + get_max_endpoint (usbdev, "ISOC IN", e, + &dev->max_isoc_in, + &dev->isoc_in); + } else { + get_max_endpoint (usbdev, "ISOC OUT", e, + &dev->max_isoc_out, + &dev->isoc_out); + } + break; + } + } + } + +#if 0 + if (interface->altsetting->desc.bAlternateSetting) { + printk("selecting alt setting %d\n", + interface->altsetting->desc.bAlternateSetting); + rc = usb_set_interface (usbdev, + interface->altsetting->desc.bInterfaceNumber, + interface->altsetting->desc.bAlternateSetting); + if (rc<0) + goto err; + } +#endif + + printk("tm6000: New video device @ %s Mbps (%04x:%04x, ifnum %d)\n", + speed, + le16_to_cpu(dev->udev->descriptor.idVendor), + le16_to_cpu(dev->udev->descriptor.idProduct), + interface->altsetting->desc.bInterfaceNumber); + +/* check if the the device has the iso in endpoint at the correct place */ + if (!dev->isoc_in) { + printk("tm6000: probing error: no IN ISOC endpoint!\n"); + rc= -ENODEV; + + goto err; + } + + /* save our data pointer in this interface device */ + usb_set_intfdata(interface, dev); + + printk("tm6000: Found %s\n", tm6000_boards[dev->model].name); + +#if 0 + retval = usb_register_dev(intf, &dabusb_class); + if (retval) { + usb_set_intfdata (intf, NULL); + return -ENOMEM; + } +#endif + rc=tm6000_init_dev(dev); + + if (rc<0) + goto err; + + return 0; + +err: + printk("tm6000: Error %d while registering\n", rc); + + tm6000_devused&=~(1<<nr); + usb_put_dev(usbdev); + + kfree(dev); + return rc; +} + +/* + * tm6000_usb_disconnect() + * called when the device gets diconencted + * video device will be unregistered on v4l2_close in case it is still open + */ +static void tm6000_usb_disconnect(struct usb_interface *interface) +{ + struct tm6000_core *dev = usb_get_intfdata(interface); + usb_set_intfdata(interface, NULL); + + if (!dev) + return; + + printk("tm6000: disconnecting %s\n", dev->name); + + mutex_lock(&dev->lock); + +#ifdef CONFIG_VIDEO_TM6000_DVB + if(dev->dvb) { + tm6000_dvb_unregister(dev); + kfree(dev->dvb); + } +#endif + + tm6000_v4l2_unregister(dev); + + tm6000_i2c_unregister(dev); + + v4l2_device_unregister(&dev->v4l2_dev); + +// wake_up_interruptible_all(&dev->open); + + dev->state |= DEV_DISCONNECTED; + + usb_put_dev(dev->udev); + + mutex_unlock(&dev->lock); + kfree(dev); +} + +static struct usb_driver tm6000_usb_driver = { +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .owner = THIS_MODULE, +#endif + .name = "tm6000", + .probe = tm6000_usb_probe, + .disconnect = tm6000_usb_disconnect, + .id_table = tm6000_id_table, +}; + +static int __init tm6000_module_init(void) +{ + int result; + + printk(KERN_INFO "tm6000" " v4l2 driver version %d.%d.%d loaded\n", + (TM6000_VERSION >> 16) & 0xff, + (TM6000_VERSION >> 8) & 0xff, TM6000_VERSION & 0xff); + + /* register this driver with the USB subsystem */ + result = usb_register(&tm6000_usb_driver); + if (result) + printk("tm6000" + " usb_register failed. Error number %d.\n", result); + + return result; +} + +static void __exit tm6000_module_exit(void) +{ + /* deregister at USB subsystem */ + usb_deregister(&tm6000_usb_driver); +} + +module_init(tm6000_module_init); +module_exit(tm6000_module_exit); + +MODULE_DESCRIPTION("Trident TVMaster TM5600/TM6000 USB2 adapter"); +MODULE_AUTHOR("Mauro Carvalho Chehab"); +MODULE_LICENSE("GPL"); diff --git a/linux/drivers/staging/tm6000/tm6000-core.c b/linux/drivers/staging/tm6000/tm6000-core.c new file mode 100644 index 000000000..5b17a851e --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-core.c @@ -0,0 +1,523 @@ +/* + tm6000-core.c - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - DVB-T support + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> +#include <linux/video_decoder.h> +#include "compat.h" +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> + +#define USB_TIMEOUT 5*HZ /* ms */ + +int tm6000_read_write_usb (struct tm6000_core *dev, u8 req_type, u8 req, + u16 value, u16 index, u8 *buf, u16 len) +{ + int ret, i; + unsigned int pipe; + static int ini=0, last=0, n=0; + u8 *data=NULL; + + if (len) + data = kzalloc(len, GFP_KERNEL); + +#if 0 + if (dev->state & DEV_DISCONNECTED) + return(-ENODEV); +#endif + + if (req_type & USB_DIR_IN) + pipe=usb_rcvctrlpipe(dev->udev, 0); + else { + pipe=usb_sndctrlpipe(dev->udev, 0); + memcpy(data, buf, len); + } + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (!ini) + last=ini=jiffies; + + printk("%06i (dev %p, pipe %08x): ", n, dev->udev, pipe); + + printk( "%s: %06u ms %06u ms %02x %02x %02x %02x %02x %02x %02x %02x ", + (req_type & USB_DIR_IN)?" IN":"OUT", + jiffies_to_msecs(jiffies-last), + jiffies_to_msecs(jiffies-ini), + req_type, req,value&0xff,value>>8, index&0xff, index>>8, + len&0xff, len>>8); + last=jiffies; + n++; + + if ( !(req_type & USB_DIR_IN) ) { + printk(">>> "); + for (i=0;i<len;i++) { + printk(" %02x",buf[i]); + } + printk("\n"); + } + } + + ret = usb_control_msg(dev->udev, pipe, req, req_type, value, index, data, + len, USB_TIMEOUT); + + if (req_type & USB_DIR_IN) + memcpy(buf, data, len); + + if (tm6000_debug & V4L2_DEBUG_I2C) { + if (ret<0) { + if (req_type & USB_DIR_IN) + printk("<<< (len=%d)\n",len); + + printk("%s: Error #%d\n", __FUNCTION__, ret); + } else if (req_type & USB_DIR_IN) { + printk("<<< "); + for (i=0;i<len;i++) { + printk(" %02x",buf[i]); + } + printk("\n"); + } + } + + kfree(data); + + msleep(5); + + return ret; +} + +int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + return + tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR, + req, value, index, NULL, 0); +} + +int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[1]; + + rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 1); + + if (rc<0) + return rc; + + return *buf; +} + +int tm6000_get_reg16 (struct tm6000_core *dev, u8 req, u16 value, u16 index) +{ + int rc; + u8 buf[2]; + + rc=tm6000_read_write_usb (dev, USB_DIR_IN | USB_TYPE_VENDOR, req, + value, index, buf, 2); + + if (rc<0) + return rc; + + return buf[1]|buf[0]<<8; +} + +void tm6000_set_fourcc_format(struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc1, 0xfc); + else + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc1, 0xfd); + } else { + if (dev->fourcc == V4L2_PIX_FMT_UYVY) + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc1, 0xd0); + else + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xc1, 0x90); + } +} + +int tm6000_init_analog_mode (struct tm6000_core *dev) +{ + if (dev->dev_type == TM6010) { + int val; + + /* Enable video */ + val = tm6000_get_reg(dev, REQ_07_SET_GET_AVREG, 0xcc, 0); + val |= 0x60; + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xcc, val); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xfe, 0xcf); + +#if 0 + /* Turn xceive 3028 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6010_GPIO_3, 0x01); + msleep(11); +#endif + } else { + /* Enables soft reset */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x3f, 0x01); + + if (dev->scaler) { + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xc0, 0x20); + } else { + /* Enable Hfilter and disable TS Drop err */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xc0, 0x80); + } + + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xc3, 0x88); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xda, 0x23); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xd1, 0xc0); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xd2, 0xd8); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xd6, 0x06); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xdf, 0x1f); + + /* AP Software reset */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xff, 0x08); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xff, 0x00); + + tm6000_set_fourcc_format(dev); + + /* Disables soft reset */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x3f, 0x00); + + /* E3: Select input 0 - TV tuner */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xe3, 0x00); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xeb, 0x60); + + /* This controls input */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_2, 0x0); + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_3, 0x01); + } + msleep(20); + + /* Tuner firmware can now be loaded */ + +#if 1 + /*FIXME: Hack!!! */ + struct v4l2_frequency f; + mutex_lock(&dev->lock); + f.frequency=dev->freq; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, &f); + mutex_unlock(&dev->lock); + + msleep(100); + tm6000_set_standard (dev, &dev->norm); + tm6000_set_audio_bitrate (dev,48000); +#endif + + return 0; +} + +int tm6000_init_digital_mode (struct tm6000_core *dev) +{ + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00ff, 0x08); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00ff, 0x00); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x003f, 0x01); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00df, 0x08); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00e2, 0x0c); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00e8, 0xff); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00eb, 0xd8); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00c0, 0x40); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00c1, 0xd0); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00c3, 0x09); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00da, 0x37); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00d1, 0xd8); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00d2, 0xc0); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00d6, 0x60); + + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00e2, 0x0c); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00e8, 0xff); + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x00eb, 0x08); + msleep(50); + + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(50); + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x01); + msleep(50); + tm6000_set_reg (dev, REQ_04_EN_DISABLE_MCU_INT, 0x0020, 0x00); + msleep(100); + + return 0; +} + +struct reg_init { + u8 req; + u8 reg; + u8 val; +}; + +/* The meaning of those initializations are unknown */ +struct reg_init tm6000_init_tab[] = { + /* REG VALUE */ + { REQ_07_SET_GET_AVREG, 0xdf, 0x1f }, + { REQ_07_SET_GET_AVREG, 0xff, 0x08 }, + { REQ_07_SET_GET_AVREG, 0xff, 0x00 }, + { REQ_07_SET_GET_AVREG, 0xd5, 0x4f }, + { REQ_07_SET_GET_AVREG, 0xda, 0x23 }, + { REQ_07_SET_GET_AVREG, 0xdb, 0x08 }, + { REQ_07_SET_GET_AVREG, 0xe2, 0x00 }, + { REQ_07_SET_GET_AVREG, 0xe3, 0x10 }, + { REQ_07_SET_GET_AVREG, 0xe5, 0x00 }, + { REQ_07_SET_GET_AVREG, 0xe8, 0x00 }, + { REQ_07_SET_GET_AVREG, 0xeb, 0x64 }, /* 48000 bits/sample, external input */ + { REQ_07_SET_GET_AVREG, 0xee, 0xc2 }, + { REQ_07_SET_GET_AVREG, 0x3f, 0x01 }, /* Start of soft reset */ + { REQ_07_SET_GET_AVREG, 0x00, 0x00 }, + { REQ_07_SET_GET_AVREG, 0x01, 0x07 }, + { REQ_07_SET_GET_AVREG, 0x02, 0x5f }, + { REQ_07_SET_GET_AVREG, 0x03, 0x00 }, + { REQ_07_SET_GET_AVREG, 0x05, 0x64 }, + { REQ_07_SET_GET_AVREG, 0x07, 0x01 }, + { REQ_07_SET_GET_AVREG, 0x08, 0x82 }, + { REQ_07_SET_GET_AVREG, 0x09, 0x36 }, + { REQ_07_SET_GET_AVREG, 0x0a, 0x50 }, + { REQ_07_SET_GET_AVREG, 0x0c, 0x6a }, + { REQ_07_SET_GET_AVREG, 0x11, 0xc9 }, + { REQ_07_SET_GET_AVREG, 0x12, 0x07 }, + { REQ_07_SET_GET_AVREG, 0x13, 0x3b }, + { REQ_07_SET_GET_AVREG, 0x14, 0x47 }, + { REQ_07_SET_GET_AVREG, 0x15, 0x6f }, + { REQ_07_SET_GET_AVREG, 0x17, 0xcd }, + { REQ_07_SET_GET_AVREG, 0x18, 0x1e }, + { REQ_07_SET_GET_AVREG, 0x19, 0x8b }, + { REQ_07_SET_GET_AVREG, 0x1a, 0xa2 }, + { REQ_07_SET_GET_AVREG, 0x1b, 0xe9 }, + { REQ_07_SET_GET_AVREG, 0x1c, 0x1c }, + { REQ_07_SET_GET_AVREG, 0x1d, 0xcc }, + { REQ_07_SET_GET_AVREG, 0x1e, 0xcc }, + { REQ_07_SET_GET_AVREG, 0x1f, 0xcd }, + { REQ_07_SET_GET_AVREG, 0x20, 0x3c }, + { REQ_07_SET_GET_AVREG, 0x21, 0x3c }, + { REQ_07_SET_GET_AVREG, 0x2d, 0x48 }, + { REQ_07_SET_GET_AVREG, 0x2e, 0x88 }, + { REQ_07_SET_GET_AVREG, 0x30, 0x22 }, + { REQ_07_SET_GET_AVREG, 0x31, 0x61 }, + { REQ_07_SET_GET_AVREG, 0x32, 0x74 }, + { REQ_07_SET_GET_AVREG, 0x33, 0x1c }, + { REQ_07_SET_GET_AVREG, 0x34, 0x74 }, + { REQ_07_SET_GET_AVREG, 0x35, 0x1c }, + { REQ_07_SET_GET_AVREG, 0x36, 0x7a }, + { REQ_07_SET_GET_AVREG, 0x37, 0x26 }, + { REQ_07_SET_GET_AVREG, 0x38, 0x40 }, + { REQ_07_SET_GET_AVREG, 0x39, 0x0a }, + { REQ_07_SET_GET_AVREG, 0x42, 0x55 }, + { REQ_07_SET_GET_AVREG, 0x51, 0x11 }, + { REQ_07_SET_GET_AVREG, 0x55, 0x01 }, + { REQ_07_SET_GET_AVREG, 0x57, 0x02 }, + { REQ_07_SET_GET_AVREG, 0x58, 0x35 }, + { REQ_07_SET_GET_AVREG, 0x59, 0xa0 }, + { REQ_07_SET_GET_AVREG, 0x80, 0x15 }, + { REQ_07_SET_GET_AVREG, 0x82, 0x42 }, + { REQ_07_SET_GET_AVREG, 0xc1, 0xd0 }, + { REQ_07_SET_GET_AVREG, 0xc3, 0x88 }, + { REQ_07_SET_GET_AVREG, 0x3f, 0x00 }, /* End of the soft reset */ + { REQ_05_SET_GET_USBREG, 0x18, 0x00 }, +}; + +struct reg_init tm6010_init_tab[] = { + { REQ_07_SET_GET_AVREG, 0xc0, 0x00 }, + { REQ_07_SET_GET_AVREG, 0xc4, 0xa0 }, + { REQ_07_SET_GET_AVREG, 0xc6, 0x40 }, + { REQ_07_SET_GET_AVREG, 0xca, 0x31 }, + { REQ_07_SET_GET_AVREG, 0xcc, 0xe1 }, + { REQ_07_SET_GET_AVREG, 0xe0, 0x03 }, + { REQ_07_SET_GET_AVREG, 0xfe, 0x7f }, + + { REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0 }, + { REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf4 }, + { REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf8 }, + { REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x00 }, + { REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf2 }, + { REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xf0 }, + { REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2 }, + { REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x60 }, + { REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc }, + + { REQ_07_SET_GET_AVREG, 0x3f, 0x01 }, + { REQ_07_SET_GET_AVREG, 0x00, 0x00 }, + { REQ_07_SET_GET_AVREG, 0x01, 0x07 }, + { REQ_07_SET_GET_AVREG, 0x02, 0x5f }, + { REQ_07_SET_GET_AVREG, 0x03, 0x00 }, + { REQ_07_SET_GET_AVREG, 0x05, 0x64 }, + { REQ_07_SET_GET_AVREG, 0x07, 0x01 }, + { REQ_07_SET_GET_AVREG, 0x08, 0x82 }, + { REQ_07_SET_GET_AVREG, 0x09, 0x36 }, + { REQ_07_SET_GET_AVREG, 0x0a, 0x50 }, + { REQ_07_SET_GET_AVREG, 0x0c, 0x6a }, + { REQ_07_SET_GET_AVREG, 0x11, 0xc9 }, + { REQ_07_SET_GET_AVREG, 0x12, 0x07 }, + { REQ_07_SET_GET_AVREG, 0x13, 0x3b }, + { REQ_07_SET_GET_AVREG, 0x14, 0x47 }, + { REQ_07_SET_GET_AVREG, 0x15, 0x6f }, + { REQ_07_SET_GET_AVREG, 0x17, 0xcd }, + { REQ_07_SET_GET_AVREG, 0x18, 0x1e }, + { REQ_07_SET_GET_AVREG, 0x19, 0x8b }, + { REQ_07_SET_GET_AVREG, 0x1a, 0xa2 }, + { REQ_07_SET_GET_AVREG, 0x1b, 0xe9 }, + { REQ_07_SET_GET_AVREG, 0x1c, 0x1c }, + { REQ_07_SET_GET_AVREG, 0x1d, 0xcc }, + { REQ_07_SET_GET_AVREG, 0x1e, 0xcc }, + { REQ_07_SET_GET_AVREG, 0x1f, 0xcd }, + { REQ_07_SET_GET_AVREG, 0x20, 0x3c }, + { REQ_07_SET_GET_AVREG, 0x21, 0x3c }, + { REQ_07_SET_GET_AVREG, 0x2d, 0x48 }, + { REQ_07_SET_GET_AVREG, 0x2e, 0x88 }, + { REQ_07_SET_GET_AVREG, 0x30, 0x22 }, + { REQ_07_SET_GET_AVREG, 0x31, 0x61 }, + { REQ_07_SET_GET_AVREG, 0x32, 0x74 }, + { REQ_07_SET_GET_AVREG, 0x33, 0x1c }, + { REQ_07_SET_GET_AVREG, 0x34, 0x74 }, + { REQ_07_SET_GET_AVREG, 0x35, 0x1c }, + { REQ_07_SET_GET_AVREG, 0x36, 0x7a }, + { REQ_07_SET_GET_AVREG, 0x37, 0x26 }, + { REQ_07_SET_GET_AVREG, 0x38, 0x40 }, + { REQ_07_SET_GET_AVREG, 0x39, 0x0a }, + { REQ_07_SET_GET_AVREG, 0x42, 0x55 }, + { REQ_07_SET_GET_AVREG, 0x51, 0x11 }, + { REQ_07_SET_GET_AVREG, 0x55, 0x01 }, + { REQ_07_SET_GET_AVREG, 0x57, 0x02 }, + { REQ_07_SET_GET_AVREG, 0x58, 0x35 }, + { REQ_07_SET_GET_AVREG, 0x59, 0xa0 }, + { REQ_07_SET_GET_AVREG, 0x80, 0x15 }, + { REQ_07_SET_GET_AVREG, 0x82, 0x42 }, + { REQ_07_SET_GET_AVREG, 0xc1, 0xd0 }, + { REQ_07_SET_GET_AVREG, 0xc3, 0x88 }, + { REQ_07_SET_GET_AVREG, 0x3f, 0x00 }, + + { REQ_05_SET_GET_USBREG, 0x18, 0x00 }, + + /* set remote wakeup key:any key wakeup */ + { REQ_07_SET_GET_AVREG, 0xe5, 0xfe }, + { REQ_07_SET_GET_AVREG, 0xda, 0xff }, +}; + +int tm6000_init (struct tm6000_core *dev) +{ + int board, rc=0, i, size; + struct reg_init *tab; + + if (dev->dev_type == TM6010) { + tab = tm6010_init_tab; + size = ARRAY_SIZE(tm6010_init_tab); + } else { + tab = tm6000_init_tab; + size = ARRAY_SIZE(tm6000_init_tab); + } + + /* Load board's initialization table */ + for (i=0; i< size; i++) { + rc= tm6000_set_reg (dev, tab[i].req, tab[i].reg, tab[i].val); + if (rc<0) { + printk (KERN_ERR "Error %i while setting req %d, " + "reg %d to value %d\n", rc, + tab[i].req,tab[i].reg, tab[i].val); + return rc; + } + } + + msleep(5); /* Just to be conservative */ + + /* Check board version - maybe 10Moons specific */ + board=tm6000_get_reg16 (dev, 0x40, 0, 0); + if (board >=0) { + printk (KERN_INFO "Board version = 0x%04x\n",board); + } else { + printk (KERN_ERR "Error %i while retrieving board version\n",board); + } + + if (dev->dev_type == TM6010) { + /* Turn xceive 3028 on */ + tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, TM6010_GPIO_3, 0x01); + msleep(11); + } + + /* Reset GPIO1 and GPIO4. */ + for (i=0; i< 2; i++) { + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->tuner_reset_gpio, 0x00); + if (rc<0) { + printk (KERN_ERR "Error %i doing GPIO1 reset\n",rc); + return rc; + } + + msleep(10); /* Just to be conservative */ + rc = tm6000_set_reg(dev, REQ_03_SET_GET_MCU_PIN, + dev->tuner_reset_gpio, 0x01); + if (rc<0) { + printk (KERN_ERR "Error %i doing GPIO1 reset\n",rc); + return rc; + } + + msleep(10); + rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_4, 0); + if (rc<0) { + printk (KERN_ERR "Error %i doing GPIO4 reset\n",rc); + return rc; + } + + msleep(10); + rc=tm6000_set_reg (dev, REQ_03_SET_GET_MCU_PIN, TM6000_GPIO_4, 1); + if (rc<0) { + printk (KERN_ERR "Error %i doing GPIO4 reset\n",rc); + return rc; + } + + if (!i) { + rc=tm6000_get_reg16(dev, 0x40,0,0); + if (rc>=0) { + printk ("board=%d\n", rc); + } + } + } + + msleep(50); + + return 0; +} + +int tm6000_set_audio_bitrate(struct tm6000_core *dev, int bitrate) +{ + int val; + + val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, 0x0); +printk("Original value=%d\n",val); + if (val<0) + return val; + + val &= 0x0f; /* Preserve the audio input control bits */ + switch (bitrate) { + case 44100: + val|=0xd0; + dev->audio_bitrate=bitrate; + break; + case 48000: + val|=0x60; + dev->audio_bitrate=bitrate; + break; + } + val=tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0xeb, val); + + return val; +} +EXPORT_SYMBOL_GPL(tm6000_set_audio_bitrate); diff --git a/linux/drivers/staging/tm6000/tm6000-dvb.c b/linux/drivers/staging/tm6000/tm6000-dvb.c new file mode 100644 index 000000000..8b117b3f5 --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-dvb.c @@ -0,0 +1,326 @@ +/* + tm6000-dvb.c - dvb-t support for TM5600/TM6000 USB video capture devices + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/usb.h> + +#include "tm6000.h" +#include "tm6000-regs.h" + +#include "hack.h" + +#include "zl10353.h" + +#include <media/tuner.h> + +#include "tuner-xc2028.h" + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +static void tm6000_urb_received(struct urb *urb, struct pt_regs *ptregs) +#else +static void tm6000_urb_received(struct urb *urb) +#endif +{ + int ret; + struct tm6000_core* dev = urb->context; + + if(urb->status != 0){ + printk(KERN_ERR "tm6000: status != 0\n"); + } + else if(urb->actual_length>0){ + dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer, + urb->actual_length); + } + + if(dev->dvb->streams > 0) { + ret = usb_submit_urb(urb, GFP_ATOMIC); + if(ret < 0) { + printk(KERN_ERR "tm6000: error %s\n", __FUNCTION__); + kfree(urb->transfer_buffer); + usb_free_urb(urb); + } + } +} + +int tm6000_start_stream(struct tm6000_core *dev) +{ + int ret; + unsigned int pipe, maxPaketSize; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got start stream request %s\n",__FUNCTION__); + + tm6000_init_digital_mode(dev); + +/* + ret = tm6000_set_led_status(tm6000_dev, 0x1); + if(ret < 0) { + return -1; + } +*/ + + dvb->bulk_urb = usb_alloc_urb(0, GFP_KERNEL); + if(dvb->bulk_urb == NULL) { + printk(KERN_ERR "tm6000: couldn't allocate urb\n"); + return -ENOMEM; + } + + maxPaketSize = dev->bulk_in->desc.wMaxPacketSize; + + dvb->bulk_urb->transfer_buffer = kzalloc(maxPaketSize, GFP_KERNEL); + if(dvb->bulk_urb->transfer_buffer == NULL) { + usb_free_urb(dvb->bulk_urb); + printk(KERN_ERR "tm6000: couldn't allocate transfer buffer!\n"); + return -ENOMEM; + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in->desc.bEndpointAddress + & USB_ENDPOINT_NUMBER_MASK); + + usb_fill_bulk_urb(dvb->bulk_urb, dev->udev, pipe, + dvb->bulk_urb->transfer_buffer, + maxPaketSize, + tm6000_urb_received, dev); + + ret = usb_set_interface(dev->udev, 0, 1); + if(ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during set interface\n", ret, __FUNCTION__); + return ret; + } + + ret = usb_clear_halt(dev->udev, pipe); + if(ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during pipe reset\n",ret,__FUNCTION__); + return ret; + } + else { + printk(KERN_ERR "tm6000: pipe resetted\n"); + } + +// mutex_lock(&tm6000_driver.open_close_mutex); + ret = usb_submit_urb(dvb->bulk_urb, GFP_KERNEL); + + +// mutex_unlock(&tm6000_driver.open_close_mutex); + if (ret) { + printk(KERN_ERR "tm6000: submit of urb failed (error=%i)\n",ret); + + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + return ret; + } + + return 0; +} + +void tm6000_stop_stream(struct tm6000_core *dev) +{ + int ret; + struct tm6000_dvb *dvb = dev->dvb; + +// tm6000_set_led_status(tm6000_dev, 0x0); + + ret = usb_set_interface(dev->udev, 0, 0); + if(ret < 0) { + printk(KERN_ERR "tm6000: error %i in %s during set interface\n",ret,__FUNCTION__); + } + + if(dvb->bulk_urb) { + usb_kill_urb(dvb->bulk_urb); + kfree(dvb->bulk_urb->transfer_buffer); + usb_free_urb(dvb->bulk_urb); + dvb->bulk_urb = NULL; + } +} + +int tm6000_start_feed(struct dvb_demux_feed *feed) +{ + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + printk(KERN_INFO "tm6000: got start feed request %s\n",__FUNCTION__); + + mutex_lock(&dvb->mutex); + if(dvb->streams == 0) { + dvb->streams = 1; +// mutex_init(&tm6000_dev->streaming_mutex); + tm6000_start_stream(dev); + } + else { + ++(dvb->streams); + } + mutex_unlock(&dvb->mutex); + + return 0; +} + +int tm6000_stop_feed(struct dvb_demux_feed *feed) { + struct dvb_demux *demux = feed->demux; + struct tm6000_core *dev = demux->priv; + struct tm6000_dvb *dvb = dev->dvb; + + printk(KERN_INFO "tm6000: got stop feed request %s\n",__FUNCTION__); + + mutex_lock(&dvb->mutex); + --dvb->streams; + + if(0 == dvb->streams) { + tm6000_stop_stream(dev); +// mutex_destroy(&tm6000_dev->streaming_mutex); + } + mutex_unlock(&dvb->mutex); +// mutex_destroy(&tm6000_dev->streaming_mutex); + + return 0; +} + +int tm6000_dvb_attach_frontend(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if(dev->caps.has_zl10353) { + struct zl10353_config config = + {.demod_address = dev->demod_addr >> 1, + .no_tuner = 1, +// .input_frequency = 0x19e9, +// .r56_agc_targets = 0x1c, + }; + + dvb->frontend = pseudo_zl10353_attach(dev, &config, + &dev->i2c_adap); + } + else { + printk(KERN_ERR "tm6000: no frontend defined for the device!\n"); + return -1; + } + + return (!dvb->frontend) ? -1 : 0; +} + +DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr); + +int tm6000_dvb_register(struct tm6000_core *dev) +{ + int ret = -1; + struct tm6000_dvb *dvb = dev->dvb; + + mutex_init(&dvb->mutex); + + dvb->streams = 0; + + /* attach the frontend */ + ret = tm6000_dvb_attach_frontend(dev); + if(ret < 0) { + printk(KERN_ERR "tm6000: couldn't attach the frontend!\n"); + goto err; + } + + ret = dvb_register_adapter(&dvb->adapter, "Trident TVMaster 6000 DVB-T", + THIS_MODULE, &dev->udev->dev, adapter_nr); + dvb->adapter.priv = dev; + + if (dvb->frontend) { + struct xc2028_config cfg = { + .i2c_adap = &dev->i2c_adap, + .i2c_addr = dev->tuner_addr, + }; + + ret = dvb_register_frontend(&dvb->adapter, dvb->frontend); + if (ret < 0) { + printk(KERN_ERR + "tm6000: couldn't register frontend\n"); + goto adapter_err; + } + + if (!dvb_attach(xc2028_attach, dvb->frontend, &cfg)) { + printk(KERN_ERR "tm6000: couldn't register " + "frontend (xc3028)\n"); + ret = -EINVAL; + goto frontend_err; + } + printk(KERN_INFO "tm6000: XC2028/3028 asked to be " + "attached to frontend!\n"); + } else { + printk(KERN_ERR "tm6000: no frontend found\n"); + } + + dvb->demux.dmx.capabilities = DMX_TS_FILTERING | DMX_SECTION_FILTERING + | DMX_MEMORY_BASED_FILTERING; + dvb->demux.priv = dev; + dvb->demux.filternum = 256; + dvb->demux.feednum = 256; + dvb->demux.start_feed = tm6000_start_feed; + dvb->demux.stop_feed = tm6000_stop_feed; + dvb->demux.write_to_decoder = NULL; + ret = dvb_dmx_init(&dvb->demux); + if(ret < 0) { + printk("tm6000: dvb_dmx_init failed (errno = %d)\n", ret); + goto frontend_err; + } + + dvb->dmxdev.filternum = dev->dvb->demux.filternum; + dvb->dmxdev.demux = &dev->dvb->demux.dmx; + dvb->dmxdev.capabilities = 0; + + ret = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter); + if(ret < 0) { + printk("tm6000: dvb_dmxdev_init failed (errno = %d)\n", ret); + goto dvb_dmx_err; + } + + return 0; + +dvb_dmx_err: + dvb_dmx_release(&dvb->demux); +frontend_err: + if(dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } +adapter_err: + dvb_unregister_adapter(&dvb->adapter); +err: + return ret; +} + +void tm6000_dvb_unregister(struct tm6000_core *dev) +{ + struct tm6000_dvb *dvb = dev->dvb; + + if(dvb->bulk_urb != NULL) { + struct urb *bulk_urb = dvb->bulk_urb; + + kfree(bulk_urb->transfer_buffer); + bulk_urb->transfer_buffer = NULL; + usb_unlink_urb(bulk_urb); + usb_free_urb(bulk_urb); + } + +// mutex_lock(&tm6000_driver.open_close_mutex); + if(dvb->frontend) { + dvb_frontend_detach(dvb->frontend); + dvb_unregister_frontend(dvb->frontend); + } + + dvb_dmxdev_release(&dvb->dmxdev); + dvb_dmx_release(&dvb->demux); + dvb_unregister_adapter(&dvb->adapter); + mutex_destroy(&dvb->mutex); +// mutex_unlock(&tm6000_driver.open_close_mutex); + +} diff --git a/linux/drivers/staging/tm6000/tm6000-i2c.c b/linux/drivers/staging/tm6000/tm6000-i2c.c new file mode 100644 index 000000000..818c553b6 --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-i2c.c @@ -0,0 +1,255 @@ +/* + tm6000-i2c.c - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - Fix SMBus Read Byte command + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/usb.h> +#include <linux/i2c.h> + +#include "compat.h" +#include "tm6000.h" +#include "tm6000-regs.h" +#include <media/v4l2-common.h> +#include <media/tuner.h> +#include "tuner-xc2028.h" + + +/*FIXME: Hack to avoid needing to patch i2c-id.h */ +#define I2C_HW_B_TM6000 I2C_HW_B_EM28XX +/* ----------------------------------------------------------- */ + +static unsigned int i2c_debug = 0; +module_param(i2c_debug, int, 0644); +MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]"); + +#define i2c_dprintk(lvl,fmt, args...) if (i2c_debug>=lvl) do{ \ + printk(KERN_DEBUG "%s at %s: " fmt, \ + dev->name, __FUNCTION__ , ##args); } while (0) + +static int tm6000_i2c_xfer(struct i2c_adapter *i2c_adap, + struct i2c_msg msgs[], int num) +{ + struct tm6000_core *dev = i2c_adap->algo_data; + int addr, rc, i, byte; + + if (num <= 0) + return 0; + for (i = 0; i < num; i++) { + addr = (msgs[i].addr << 1) & 0xff; + i2c_dprintk(2,"%s %s addr=0x%x len=%d:", + (msgs[i].flags & I2C_M_RD) ? "read" : "write", + i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len); + if (msgs[i].flags & I2C_M_RD) { + /* read request without preceding register selection */ + /* + * The TM6000 only supports a read transaction + * immediately after a 1 or 2 byte write to select + * a register. We cannot fulfil this request. + */ + i2c_dprintk(2, " read without preceding write not" + " supported"); + rc = -EOPNOTSUPP; + goto err; + } else if (i + 1 < num && msgs[i].len <= 2 && + (msgs[i + 1].flags & I2C_M_RD) && + msgs[i].addr == msgs[i + 1].addr) { + /* 1 or 2 byte write followed by a read */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + i2c_dprintk(2, "; joined to read %s len=%d:", + i == num - 2 ? "stop" : "nonstop", + msgs[i + 1].len); + rc = tm6000_read_write_usb (dev, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + msgs[i].len == 1 ? REQ_16_SET_GET_I2C_WR1_RDN + : REQ_14_SET_GET_I2C_WR2_RDN, + addr | msgs[i].buf[0] << 8, + msgs[i].len == 1 ? 0 : msgs[i].buf[1], + msgs[i + 1].buf, msgs[i + 1].len); + i++; + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + } else { + /* write bytes */ + if (i2c_debug >= 2) + for (byte = 0; byte < msgs[i].len; byte++) + printk(" %02x", msgs[i].buf[byte]); + rc = tm6000_read_write_usb(dev, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, + addr | msgs[i].buf[0] << 8, 0, + msgs[i].buf + 1, msgs[i].len - 1); + } + if (i2c_debug >= 2) + printk("\n"); + if (rc < 0) + goto err; + } + + return num; +err: + i2c_dprintk(2," ERROR: %i\n", rc); + return rc; +} + +static int tm6000_i2c_eeprom(struct tm6000_core *dev, + unsigned char *eedata, int len) +{ + int i, rc; + unsigned char *p = eedata; + unsigned char bytes[17]; + + dev->i2c_client.addr = 0xa0 >> 1; + + bytes[16] = '\0'; + for (i = 0; i < len; ) { +#if 0 + rc = i2c_master_recv(&dev->i2c_client, p, 1); +#else + *p = i; + rc = tm6000_read_write_usb (dev, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + REQ_16_SET_GET_I2C_WR1_RDN, 0xa0 | i<<8, 0, p, 1); +#endif + if (rc < 1) { + if (p == eedata) + goto noeeprom; + else { + printk(KERN_WARNING + "%s: i2c eeprom read error (err=%d)\n", + dev->name, rc); + } + return -1; + } + p++; + if (0 == (i % 16)) + printk(KERN_INFO "%s: i2c eeprom %02x:", dev->name, i); + printk(" %02x", eedata[i]); + if ((eedata[i] >= ' ') && (eedata[i] <= 'z')) { + bytes[i%16] = eedata[i]; + } else { + bytes[i%16]='.'; + } + + i++; + + if (0 == (i % 16)) { + bytes[16] = '\0'; + printk(" %s\n", bytes); + } + } + if (0 != (i%16)) { + bytes[i%16] = '\0'; + for (i %= 16; i < 16; i++) + printk(" "); + } + printk(" %s\n", bytes); + + return 0; + +noeeprom: + printk(KERN_INFO "%s: Huh, no eeprom present (err=%d)?\n", + dev->name, rc); + return rc; +} + +/* ----------------------------------------------------------- */ + +/* + * functionality() + */ +static u32 functionality(struct i2c_adapter *adap) +{ + return I2C_FUNC_SMBUS_EMUL; +} + +#define mass_write(addr, reg, data...) \ + { const static u8 _val[] = data; \ + rc=tm6000_read_write_usb(dev,USB_DIR_OUT | USB_TYPE_VENDOR, \ + REQ_16_SET_GET_I2C_WR1_RDN,(reg<<8)+addr, 0x00, (u8 *) _val, \ + ARRAY_SIZE(_val)); \ + if (rc<0) { \ + printk(KERN_ERR "Error on line %d: %d\n",__LINE__,rc); \ + return rc; \ + } \ + msleep (10); \ + } + +static struct i2c_algorithm tm6000_algo = { + .master_xfer = tm6000_i2c_xfer, + .functionality = functionality, +}; + +static struct i2c_adapter tm6000_adap_template = { + .owner = THIS_MODULE, +#ifdef I2C_CLASS_TV_ANALOG + .class = I2C_CLASS_TV_ANALOG, +#endif + .name = "tm6000", + .id = I2C_HW_B_TM6000, + .algo = &tm6000_algo, +}; + +static struct i2c_client tm6000_client_template = { + .name = "tm6000 internal", +#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,15) + .flags = I2C_CLIENT_ALLOW_USE, +#endif +}; + +/* ----------------------------------------------------------- */ + +/* + * tm6000_i2c_register() + * register i2c bus + */ +int tm6000_i2c_register(struct tm6000_core *dev) +{ + unsigned char eedata[256]; + + dev->i2c_adap = tm6000_adap_template; + dev->i2c_adap.dev.parent = &dev->udev->dev; + strcpy(dev->i2c_adap.name, dev->name); + dev->i2c_adap.algo_data = dev; + i2c_add_adapter(&dev->i2c_adap); + + dev->i2c_client = tm6000_client_template; + dev->i2c_client.adapter = &dev->i2c_adap; + + i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev); + + tm6000_i2c_eeprom(dev, eedata, sizeof(eedata)); + + return 0; +} + +/* + * tm6000_i2c_unregister() + * unregister i2c_bus + */ +int tm6000_i2c_unregister(struct tm6000_core *dev) +{ + i2c_del_adapter(&dev->i2c_adap); + return 0; +} diff --git a/linux/drivers/staging/tm6000/tm6000-regs.h b/linux/drivers/staging/tm6000/tm6000-regs.h new file mode 100644 index 000000000..85acc07f6 --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-regs.h @@ -0,0 +1,86 @@ +/* + tm6000-regs.h - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * Define TV Master TM5600/TM6000 Request codes + */ +#define REQ_00_SET_IR_VALUE 0 +#define REQ_01_SET_WAKEUP_IRCODE 1 +#define REQ_02_GET_IR_CODE 2 +#define REQ_03_SET_GET_MCU_PIN 3 +#define REQ_04_EN_DISABLE_MCU_INT 4 +#define REQ_05_SET_GET_USBREG 5 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_06_SET_GET_USBREG_BIT 6 +#define REQ_07_SET_GET_AVREG 7 + /* Write: RegNum, Value, 0 */ + /* Read : RegNum, Value, 1, RegStatus */ +#define REQ_08_SET_GET_AVREG_BIT 8 +#define REQ_09_SET_GET_TUNER_FQ 9 +#define REQ_10_SET_TUNER_SYSTEM 10 +#define REQ_11_SET_EEPROM_ADDR 11 +#define REQ_12_SET_GET_EEPROMBYTE 12 +#define REQ_13_GET_EEPROM_SEQREAD 13 +#define REQ_14_SET_GET_I2C_WR2_RDN 14 +#define REQ_15_SET_GET_I2CBYTE 15 + /* Write: Subaddr, Slave Addr, value, 0 */ + /* Read : Subaddr, Slave Addr, value, 1 */ +#define REQ_16_SET_GET_I2C_WR1_RDN 16 + /* Subaddr, Slave Addr, 0, length */ +#define REQ_17_SET_GET_I2CFP 17 + /* Write: Slave Addr, register, value */ + /* Read : Slave Addr, register, 2, data */ + +/* + * Define TV Master TM5600/TM6000 GPIO lines + */ + +#define TM6000_GPIO_CLK 0x101 +#define TM6000_GPIO_DATA 0x100 + +#define TM6000_GPIO_1 0x102 +#define TM6000_GPIO_2 0x103 +#define TM6000_GPIO_3 0x104 +#define TM6000_GPIO_4 0x300 +#define TM6000_GPIO_5 0x301 +#define TM6000_GPIO_6 0x304 +#define TM6000_GPIO_7 0x305 + +/* tm6010 defines GPIO with different values */ +#define TM6010_GPIO_0 0x0102 +#define TM6010_GPIO_1 0x0103 +#define TM6010_GPIO_2 0x0104 +#define TM6010_GPIO_3 0x0105 +#define TM6010_GPIO_4 0x0106 +#define TM6010_GPIO_5 0x0107 +#define TM6010_GPIO_6 0x0300 +#define TM6010_GPIO_7 0x0301 +#define TM6010_GPIO_9 0x0305 +/* + * Define TV Master TM5600/TM6000 URB message codes and length + */ + +enum { + TM6000_URB_MSG_VIDEO=1, + TM6000_URB_MSG_AUDIO, + TM6000_URB_MSG_VBI, + TM6000_URB_MSG_PTS, + TM6000_URB_MSG_ERR, +}; diff --git a/linux/drivers/staging/tm6000/tm6000-stds.c b/linux/drivers/staging/tm6000/tm6000-stds.c new file mode 100644 index 000000000..c56444fb8 --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-stds.c @@ -0,0 +1,898 @@ +/* + tm6000-stds.c - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2007 Mauro Carvalho Chehab <mchehab@redhat.com> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include "compat.h" +#include "tm6000.h" +#include "tm6000-regs.h" + +struct tm6000_reg_settings { + unsigned char req; + unsigned char reg; + unsigned char value; +}; + +struct tm6000_std_tv_settings { + v4l2_std_id id; + struct tm6000_reg_settings sif[12]; + struct tm6000_reg_settings nosif[12]; + struct tm6000_reg_settings common[25]; +}; + +struct tm6000_std_settings { + v4l2_std_id id; + struct tm6000_reg_settings common[37]; +}; + +static struct tm6000_std_tv_settings tv_stds[] = { + { + .id = V4L2_STD_PAL_M, + .sif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x08}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x62}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfe}, + {REQ_07_SET_GET_AVREG, 0xfe, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x60}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + {0, 0, 0}, + }, + .common = { + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x04}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x00}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x83}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x0a}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xe0}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x88}, + {REQ_07_SET_GET_AVREG, 0x30, 0x20}, + {REQ_07_SET_GET_AVREG, 0x31, 0x61}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL_Nc, + .sif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x08}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x62}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfe}, + {REQ_07_SET_GET_AVREG, 0xfe, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x60}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + {0, 0, 0}, + }, + .common = { + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x36}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x02}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x91}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x1f}, + {REQ_07_SET_GET_AVREG, 0x1b, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL, + .sif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x08}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x62}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfe}, + {REQ_07_SET_GET_AVREG, 0xfe, 0xcb}, + {0, 0, 0} + }, + .nosif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x60}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + {0, 0, 0}, + }, + .common = { + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x32}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x02}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x25}, + {REQ_07_SET_GET_AVREG, 0x19, 0xd5}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x63}, + {REQ_07_SET_GET_AVREG, 0x1b, 0x50}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_SECAM, + .sif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x08}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x62}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfe}, + {REQ_07_SET_GET_AVREG, 0xfe, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x60}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + {0, 0, 0}, + }, + .common = { + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x38}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x02}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x24}, + {REQ_07_SET_GET_AVREG, 0x19, 0x92}, + {REQ_07_SET_GET_AVREG, 0x1a, 0xe8}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xed}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x18}, + {REQ_07_SET_GET_AVREG, 0x82, 0x42}, + {REQ_07_SET_GET_AVREG, 0x83, 0xFF}, + + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_NTSC, + .sif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x08}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x62}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfe}, + {REQ_07_SET_GET_AVREG, 0xfe, 0xcb}, + {0, 0, 0}, + }, + .nosif = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x60}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + {0, 0, 0}, + }, + .common = { + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x00}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0f}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x00}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x8b}, + {REQ_07_SET_GET_AVREG, 0x1a, 0xa2}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xe9}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x88}, + {REQ_07_SET_GET_AVREG, 0x30, 0x22}, + {REQ_07_SET_GET_AVREG, 0x31, 0x61}, + {REQ_07_SET_GET_AVREG, 0x33, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x42}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdd}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, +}; + +static struct tm6000_std_settings composite_stds[] = { + { + .id = V4L2_STD_PAL_M, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf4}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x04}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x00}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x83}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x0a}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xe0}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x88}, + {REQ_07_SET_GET_AVREG, 0x30, 0x20}, + {REQ_07_SET_GET_AVREG, 0x31, 0x61}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL_Nc, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf4}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x36}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x02}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x91}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x1f}, + {REQ_07_SET_GET_AVREG, 0x1b, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf4}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x32}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x02}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x25}, + {REQ_07_SET_GET_AVREG, 0x19, 0xd5}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x63}, + {REQ_07_SET_GET_AVREG, 0x1b, 0x50}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_SECAM, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf4}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x38}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x02}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x24}, + {REQ_07_SET_GET_AVREG, 0x19, 0x92}, + {REQ_07_SET_GET_AVREG, 0x1a, 0xe8}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xed}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x18}, + {REQ_07_SET_GET_AVREG, 0x82, 0x42}, + {REQ_07_SET_GET_AVREG, 0x83, 0xFF}, + + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_NTSC, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xf4}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf3}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x0f}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf1}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe8}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8b}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x00}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0f}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x00}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x8b}, + {REQ_07_SET_GET_AVREG, 0x1a, 0xa2}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xe9}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x88}, + {REQ_07_SET_GET_AVREG, 0x30, 0x22}, + {REQ_07_SET_GET_AVREG, 0x31, 0x61}, + {REQ_07_SET_GET_AVREG, 0x33, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x42}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdd}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, +}; + +static struct tm6000_std_settings svideo_stds[] = { + { + .id = V4L2_STD_PAL_M, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xfc}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x00}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8a}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x05}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x04}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x83}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x0a}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xe0}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x88}, + {REQ_07_SET_GET_AVREG, 0x30, 0x22}, + {REQ_07_SET_GET_AVREG, 0x31, 0x61}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL_Nc, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xfc}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x00}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8a}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x37}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x04}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x91}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x1f}, + {REQ_07_SET_GET_AVREG, 0x1b, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x88}, + {REQ_07_SET_GET_AVREG, 0x30, 0x22}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_PAL, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xfc}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x00}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8a}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x33}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x04}, + {REQ_07_SET_GET_AVREG, 0x07, 0x00}, + {REQ_07_SET_GET_AVREG, 0x18, 0x25}, + {REQ_07_SET_GET_AVREG, 0x19, 0xd5}, + {REQ_07_SET_GET_AVREG, 0x1a, 0x63}, + {REQ_07_SET_GET_AVREG, 0x1b, 0x50}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2a}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x0c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x52}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdc}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_SECAM, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xfc}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x00}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8a}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x39}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0e}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x03}, + {REQ_07_SET_GET_AVREG, 0x07, 0x01}, + {REQ_07_SET_GET_AVREG, 0x18, 0x24}, + {REQ_07_SET_GET_AVREG, 0x19, 0x92}, + {REQ_07_SET_GET_AVREG, 0x1a, 0xe8}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xed}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x8c}, + {REQ_07_SET_GET_AVREG, 0x30, 0x2a}, + {REQ_07_SET_GET_AVREG, 0x31, 0xc1}, + {REQ_07_SET_GET_AVREG, 0x33, 0x2c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x18}, + {REQ_07_SET_GET_AVREG, 0x82, 0x42}, + {REQ_07_SET_GET_AVREG, 0x83, 0xFF}, + + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, { + .id = V4L2_STD_NTSC, + .common = { + {REQ_08_SET_GET_AVREG_BIT, 0xe2, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xe3, 0xfc}, + {REQ_08_SET_GET_AVREG_BIT, 0xe4, 0xf8}, + {REQ_08_SET_GET_AVREG_BIT, 0xe6, 0x00}, + {REQ_08_SET_GET_AVREG_BIT, 0xea, 0xf2}, + {REQ_08_SET_GET_AVREG_BIT, 0xeb, 0xf0}, + {REQ_08_SET_GET_AVREG_BIT, 0xec, 0xc2}, + {REQ_08_SET_GET_AVREG_BIT, 0xed, 0xe0}, + {REQ_08_SET_GET_AVREG_BIT, 0xf0, 0x68}, + {REQ_08_SET_GET_AVREG_BIT, 0xf1, 0xfc}, + {REQ_07_SET_GET_AVREG, 0xfe, 0x8a}, + + {REQ_07_SET_GET_AVREG, 0x3f, 0x01}, + {REQ_07_SET_GET_AVREG, 0x00, 0x01}, + {REQ_07_SET_GET_AVREG, 0x01, 0x0f}, + {REQ_07_SET_GET_AVREG, 0x02, 0x5f}, + {REQ_07_SET_GET_AVREG, 0x03, 0x03}, + {REQ_07_SET_GET_AVREG, 0x07, 0x00}, + {REQ_07_SET_GET_AVREG, 0x17, 0x8b}, + {REQ_07_SET_GET_AVREG, 0x18, 0x1e}, + {REQ_07_SET_GET_AVREG, 0x19, 0x8b}, + {REQ_07_SET_GET_AVREG, 0x1a, 0xa2}, + {REQ_07_SET_GET_AVREG, 0x1b, 0xe9}, + {REQ_07_SET_GET_AVREG, 0x1c, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x1d, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1e, 0xcc}, + {REQ_07_SET_GET_AVREG, 0x1f, 0xcd}, + {REQ_07_SET_GET_AVREG, 0x2e, 0x88}, + {REQ_07_SET_GET_AVREG, 0x30, 0x22}, + {REQ_07_SET_GET_AVREG, 0x31, 0x61}, + {REQ_07_SET_GET_AVREG, 0x33, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x35, 0x1c}, + {REQ_07_SET_GET_AVREG, 0x82, 0x42}, + {REQ_07_SET_GET_AVREG, 0x83, 0x6F}, + + {REQ_07_SET_GET_AVREG, 0x04, 0xdd}, + {REQ_07_SET_GET_AVREG, 0x0d, 0x07}, + {REQ_07_SET_GET_AVREG, 0x3f, 0x00}, + {0, 0, 0}, + }, + }, +}; + +void tm6000_get_std_res(struct tm6000_core *dev) +{ + /* Currently, those are the only supported resoltions */ + if (dev->norm & V4L2_STD_525_60) { + dev->height = 480; + } else { + dev->height = 576; + } + dev->width = 720; +} + +static int tm6000_load_std(struct tm6000_core *dev, + struct tm6000_reg_settings *set, int max_size) +{ + int i, rc; + + /* Load board's initialization table */ + for (i = 0; max_size; i++) { + if (!set[i].req) + return 0; + + if ((dev->dev_type != TM6010) && + (set[i].req == REQ_08_SET_GET_AVREG_BIT)) + continue; + + rc = tm6000_set_reg(dev, set[i].req, set[i].reg, set[i].value); + if (rc < 0) { + printk(KERN_ERR "Error %i while setting " + "req %d, reg %d to value %d\n", + rc, set[i].req, set[i].reg, set[i].value); + return rc; + } + } + + return 0; +} + +static int tm6000_set_tv(struct tm6000_core *dev, int pos) +{ + int rc; + +#if 1 + /* FIXME: This code is for tm6010 - not tested yet - doesn't work with + tm5600 + */ + + /* FIXME: This is tuner-dependent */ + int nosif = 0; + + if (nosif) { + rc = tm6000_load_std(dev, tv_stds[pos].nosif, + sizeof(tv_stds[pos].nosif)); + } else { + rc = tm6000_load_std(dev, tv_stds[pos].sif, + sizeof(tv_stds[pos].sif)); + } + if (rc < 0) + return rc; +#endif + rc = tm6000_load_std(dev, tv_stds[pos].common, + sizeof(tv_stds[pos].common)); + + return rc; +} + +int tm6000_set_standard(struct tm6000_core *dev, v4l2_std_id * norm) +{ + int i, rc = 0; + + dev->norm = *norm; + tm6000_get_std_res(dev); + + switch (dev->input) { + case TM6000_INPUT_TV: + for (i = 0; i < ARRAY_SIZE(tv_stds); i++) { + if (*norm & tv_stds[i].id) { + rc = tm6000_set_tv(dev, i); + goto ret; + } + } + return -EINVAL; + case TM6000_INPUT_SVIDEO: + for (i = 0; i < ARRAY_SIZE(svideo_stds); i++) { + if (*norm & svideo_stds[i].id) { + rc = tm6000_load_std(dev, svideo_stds[i].common, + sizeof(svideo_stds[i]. + common)); + goto ret; + } + } + return -EINVAL; + case TM6000_INPUT_COMPOSITE: + for (i = 0; i < ARRAY_SIZE(composite_stds); i++) { + if (*norm & composite_stds[i].id) { + rc = tm6000_load_std(dev, + composite_stds[i].common, + sizeof(composite_stds[i]. + common)); + goto ret; + } + } + return -EINVAL; + } + +ret: + if (rc < 0) + return rc; + + msleep(40); + +#if 0 + /* Sets scaling */ + if (dev->dev_type != TM6010) { + /* Enables soft reset */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x3f, 0x01); + + /* Enable Hfilter and disable TS Drop err */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xc0, 0x80); + + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xc3, 0x88); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xda, 0x23); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xd1, 0xc0); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xd2, 0xd8); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xd6, 0x06); + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0xdf, 0x1f); + + tm6000_set_fourcc_format(dev); + + /* Disables soft reset */ + tm6000_set_reg(dev, REQ_07_SET_GET_AVREG, 0x3f, 0x00); + } +#endif + + return 0; +} diff --git a/linux/drivers/staging/tm6000/tm6000-usb-isoc.h b/linux/drivers/staging/tm6000/tm6000-usb-isoc.h new file mode 100644 index 000000000..9d435531e --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-usb-isoc.h @@ -0,0 +1,54 @@ +/* + tm6000-buf.c - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "compat.h" +#include <linux/videodev2.h> + +#define TM6000_URB_MSG_LEN 180 + +struct usb_isoc_ctl { + /* max packet size of isoc transaction */ + int max_pkt_size; + + /* number of allocated urbs */ + int num_bufs; + + /* urb for isoc transfers */ + struct urb **urb; + + /* transfer buffers for isoc transfer */ + char **transfer_buffer; + + /* Last buffer command and region */ + u8 cmd; + int pos, size, pktsize; + + /* Last field: ODD or EVEN? */ + int field; + + /* Stores incomplete commands */ + u32 tmp_buf; + int tmp_buf_len; + + /* Stores already requested buffers */ + struct tm6000_buffer *buf; + + /* Stores the number of received fields */ + int nfields; +}; diff --git a/linux/drivers/staging/tm6000/tm6000-video.c b/linux/drivers/staging/tm6000/tm6000-video.c new file mode 100644 index 000000000..7ce2e43a4 --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000-video.c @@ -0,0 +1,1656 @@ +/* + tm6000-video.c - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - Fixed module load/unload + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/ioport.h> +#include <linux/init.h> +#include <linux/sched.h> +#include <linux/random.h> +#include <linux/version.h> +#include <linux/usb.h> +#include "compat.h" +#include <linux/videodev2.h> +#include <media/v4l2-ioctl.h> +#include <linux/interrupt.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) +#include <linux/kthread.h> +#endif +#include <linux/highmem.h> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) +#include <linux/freezer.h> +#endif + +#include "tm6000-regs.h" +#include "tm6000.h" + +#define BUFFER_TIMEOUT msecs_to_jiffies(2000) /* 2 seconds */ + +/* Limits minimum and default number of buffers */ +#define TM6000_MIN_BUF 4 +#define TM6000_DEF_BUF 8 + +#define TM6000_MAX_ISO_PACKETS 40 /* Max number of ISO packets */ + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ + +/* Debug level */ +int tm6000_debug; + +/* supported controls */ +static struct v4l2_queryctrl tm6000_qctrl[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 255, + .step = 1, + .default_value = 54, + .flags = 0, + }, { + .id = V4L2_CID_CONTRAST, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Contrast", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 119, + .flags = 0, + }, { + .id = V4L2_CID_SATURATION, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Saturation", + .minimum = 0, + .maximum = 255, + .step = 0x1, + .default_value = 112, + .flags = 0, + }, { + .id = V4L2_CID_HUE, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Hue", + .minimum = -128, + .maximum = 127, + .step = 0x1, + .default_value = 0, + .flags = 0, + } +}; + +static int qctl_regs[ARRAY_SIZE(tm6000_qctrl)]; + +static struct tm6000_fmt format[] = { + { + .name = "4:2:2, packed, YVY2", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16, + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16, + }, { + .name = "A/V + VBI mux packet", + .fourcc = V4L2_PIX_FMT_TM6000, + .depth = 16, + } +}; + +static LIST_HEAD(tm6000_corelist); + +/* ------------------------------------------------------------------ + DMA and thread functions + ------------------------------------------------------------------*/ + +#define norm_maxw(a) 720 +#define norm_maxh(a) 576 + +#define norm_minw(a) norm_maxw(a) +#define norm_minh(a) norm_maxh(a) + +/* + * video-buf generic routine to get the next available buffer + */ +static inline void get_next_buf(struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer **buf) +{ + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); +#if 1 + char *outp; +#endif + + if (list_empty(&dma_q->active)) { + dprintk(dev, V4L2_DEBUG_QUEUE, "No active queue to serve\n"); + *buf = NULL; + return; + } + + *buf = list_entry(dma_q->active.next, + struct tm6000_buffer, vb.queue); + +#if 1 + if (!buf) + return; + + /* Cleans up buffer - Usefull for testing for frame/URB loss */ + outp = videobuf_to_vmalloc(&(*buf)->vb); + memset(outp, 0, (*buf)->vb.size); +#endif + + return; +} + +/* + * Announces that a buffer were filled and request the next + */ +static inline void buffer_filled(struct tm6000_core *dev, + struct tm6000_dmaqueue *dma_q, + struct tm6000_buffer *buf) +{ + /* Advice that buffer was filled */ + dprintk(dev, V4L2_DEBUG_ISOC, "[%p/%d] wakeup\n", buf, buf->vb.i); + buf->vb.state = VIDEOBUF_DONE; + buf->vb.field_count++; + do_gettimeofday(&buf->vb.ts); + + list_del(&buf->vb.queue); + wake_up(&buf->vb.done); +} + +const char *tm6000_msg_type[] = { + "unknown(0)", /* 0 */ + "video", /* 1 */ + "audio", /* 2 */ + "vbi", /* 3 */ + "pts", /* 4 */ + "err", /* 5 */ + "unknown(6)", /* 6 */ + "unknown(7)", /* 7 */ +}; + +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_packet(struct urb *urb, u32 header, u8 **ptr, u8 *endp, + u8 *out_p, struct tm6000_buffer **buf) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + u8 c; + unsigned int cmd, cpysize, pktsize, size, field, block, line, pos = 0; + int rc = 0; + /* FIXME: move to tm6000-isoc */ + static int last_line = -2, start_line = -2, last_field = -2; + + /* FIXME: this is the hardcoded window size + */ + unsigned int linewidth = (*buf)->vb.width << 1; + + if (!dev->isoc_ctl.cmd) { + c = (header >> 24) & 0xff; + + /* split the header fields */ + size = (((header & 0x7e) << 1) -1) *4; + block = (header >> 7) & 0xf; + field = (header >> 11) & 0x1; + line = (header >> 12) & 0x1ff; + cmd = (header >> 21) & 0x7; + + /* Validates header fields */ + if(size > TM6000_URB_MSG_LEN) + size = TM6000_URB_MSG_LEN; + + if (cmd == TM6000_URB_MSG_VIDEO) { + if ((block+1)*TM6000_URB_MSG_LEN>linewidth) + cmd = TM6000_URB_MSG_ERR; + + /* FIXME: Mounts the image as field0+field1 + * It should, instead, check if the user selected + * entrelaced or non-entrelaced mode + */ + pos= ((line<<1)+field)*linewidth + + block*TM6000_URB_MSG_LEN; + + /* Don't allow to write out of the buffer */ + if (pos+TM6000_URB_MSG_LEN > (*buf)->vb.size) { + dprintk(dev, V4L2_DEBUG_ISOC, + "ERR: size=%d, num=%d, line=%d, " + "field=%d\n", + size, block, line, field); + + cmd = TM6000_URB_MSG_ERR; + } + } else { + pos=0; + } + + /* Prints debug info */ + dprintk(dev, V4L2_DEBUG_ISOC, "size=%d, num=%d, " + " line=%d, field=%d\n", + size, block, line, field); + + if ((last_line!=line)&&(last_line+1!=line) && + (cmd != TM6000_URB_MSG_ERR) ) { + if (cmd != TM6000_URB_MSG_VIDEO) { + dprintk(dev, V4L2_DEBUG_ISOC, "cmd=%d, " + "size=%d, num=%d, line=%d, field=%d\n", + cmd, size, block, line, field); + } + if (start_line<0) + start_line=last_line; + /* Prints debug info */ + dprintk(dev, V4L2_DEBUG_ISOC, "lines= %d-%d, " + "field=%d\n", + start_line, last_line, field); + + if ((start_line<6 && last_line>200) && + (last_field != field) ) { + + dev->isoc_ctl.nfields++; + if (dev->isoc_ctl.nfields>=2) { + dev->isoc_ctl.nfields=0; + + /* Announces that a new buffer were filled */ + buffer_filled (dev, dma_q, *buf); + dprintk(dev, V4L2_DEBUG_ISOC, + "new buffer filled\n"); + get_next_buf (dma_q, buf); + if (!*buf) + return rc; + out_p = videobuf_to_vmalloc(&((*buf)->vb)); + if (!out_p) + return rc; + + pos = dev->isoc_ctl.pos = 0; + } + } + + start_line=line; + last_field=field; + } + last_line=line; + + pktsize = TM6000_URB_MSG_LEN; + } else { + /* Continue the last copy */ + cmd = dev->isoc_ctl.cmd; + size= dev->isoc_ctl.size; + pos = dev->isoc_ctl.pos; + pktsize = dev->isoc_ctl.pktsize; + } + +#if 0 /* This never happens at this point */ + if (!buf) + cmd=TM6000_URB_MSG_ERR; +#endif + cpysize = (endp-(*ptr) > size) ? size : endp - *ptr; + + if (cpysize) { + /* handles each different URB message */ + switch(cmd) { + case TM6000_URB_MSG_VIDEO: + /* Fills video buffer */ + memcpy(&out_p[pos], *ptr, cpysize); + break; + case TM6000_URB_MSG_PTS: + break; + case TM6000_URB_MSG_AUDIO: +/* Need some code to process audio */ +printk ("%ld: cmd=%s, size=%d\n", jiffies, + tm6000_msg_type[cmd],size); + break; + default: + dprintk (dev, V4L2_DEBUG_ISOC, "cmd=%s, size=%d\n", + tm6000_msg_type[cmd],size); + } + } + if (cpysize<size) { + /* End of URB packet, but cmd processing is not + * complete. Preserve the state for a next packet + */ + dev->isoc_ctl.pos = pos+cpysize; + dev->isoc_ctl.size= size-cpysize; + dev->isoc_ctl.cmd = cmd; + dev->isoc_ctl.pktsize = pktsize-cpysize; + (*ptr)+=cpysize; + } else { + dev->isoc_ctl.cmd = 0; + (*ptr)+=pktsize; + } + + return rc; +} + +static int copy_streams(u8 *data, u8 *out_p, unsigned long len, + struct urb *urb, struct tm6000_buffer **buf) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); + u8 *ptr=data, *endp=data+len; + unsigned long header=0; + int rc=0; + + for (ptr=data; ptr<endp;) { + if (!dev->isoc_ctl.cmd) { + u8 *p=(u8 *)&dev->isoc_ctl.tmp_buf; + /* FIXME: This seems very complex + * It just recovers up to 3 bytes of the header that + * might be at the previous packet + */ + if (dev->isoc_ctl.tmp_buf_len) { + while (dev->isoc_ctl.tmp_buf_len) { + if ( *(ptr+3-dev->isoc_ctl.tmp_buf_len) == 0x47) { + break; + } + p++; + dev->isoc_ctl.tmp_buf_len--; + } + if (dev->isoc_ctl.tmp_buf_len) { + memcpy (&header,p, + dev->isoc_ctl.tmp_buf_len); + memcpy (((u8 *)header)+ + dev->isoc_ctl.tmp_buf, + ptr, + 4-dev->isoc_ctl.tmp_buf_len); + ptr+=4-dev->isoc_ctl.tmp_buf_len; + goto HEADER; + } + } + /* Seek for sync */ + for (;ptr<endp-3;ptr++) { + if (*(ptr+3)==0x47) + break; + } + + if (ptr+3>=endp) { + dev->isoc_ctl.tmp_buf_len=endp-ptr; + memcpy (&dev->isoc_ctl.tmp_buf,ptr, + dev->isoc_ctl.tmp_buf_len); + dev->isoc_ctl.cmd=0; + return rc; + } + + /* Get message header */ + header=*(unsigned long *)ptr; + ptr+=4; + } +HEADER: + /* Copy or continue last copy */ + rc=copy_packet(urb,header,&ptr,endp,out_p,buf); + if (rc<0) { + buf=NULL; + printk(KERN_ERR "tm6000: buffer underrun at %ld\n", + jiffies); + return rc; + } + } + + return 0; +} +/* + * Identify the tm5600/6000 buffer header type and properly handles + */ +static int copy_multiplexed(u8 *ptr, u8 *out_p, unsigned long len, + struct urb *urb, struct tm6000_buffer **buf) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); + unsigned int pos=dev->isoc_ctl.pos,cpysize; + int rc=1; + + while (len>0) { + cpysize=min(len,(*buf)->vb.size-pos); +//printk("Copying %d bytes (max=%lu) from %p to %p[%u]\n",cpysize,(*buf)->vb.size,ptr,out_p,pos); + memcpy(&out_p[pos], ptr, cpysize); + pos+=cpysize; + ptr+=cpysize; + len-=cpysize; + if (pos >= (*buf)->vb.size) { + pos=0; + /* Announces that a new buffer were filled */ + buffer_filled (dev, dma_q, *buf); + dprintk(dev, V4L2_DEBUG_ISOC, "new buffer filled\n"); + get_next_buf (dma_q, buf); + if (!*buf) + break; + out_p = videobuf_to_vmalloc(&((*buf)->vb)); + if (!out_p) + return rc; + pos = 0; + } + } + + dev->isoc_ctl.pos=pos; + return rc; +} + +static void inline print_err_status (struct tm6000_core *dev, + int packet, int status) +{ + char *errmsg = "Unknown"; + + switch(status) { + case -ENOENT: + errmsg = "unlinked synchronuously"; + break; + case -ECONNRESET: + errmsg = "unlinked asynchronuously"; + break; + case -ENOSR: + errmsg = "Buffer error (overrun)"; + break; + case -EPIPE: + errmsg = "Stalled (device not responding)"; + break; + case -EOVERFLOW: + errmsg = "Babble (bad cable?)"; + break; + case -EPROTO: + errmsg = "Bit-stuff error (bad cable?)"; + break; + case -EILSEQ: + errmsg = "CRC/Timeout (could be anything)"; + break; + case -ETIME: + errmsg = "Device does not respond"; + break; + } + if (packet<0) { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB status %d [%s].\n", + status, errmsg); + } else { + dprintk(dev, V4L2_DEBUG_QUEUE, "URB packet %d, status %d [%s].\n", + packet, status, errmsg); + } +} + + +/* + * Controls the isoc copy of each urb packet + */ +static inline int tm6000_isoc_copy(struct urb *urb) +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev= container_of(dma_q,struct tm6000_core,vidq); + struct tm6000_buffer *buf; + int i, len=0, rc=1; + int size; + char *outp = NULL, *p; + unsigned long copied; + + get_next_buf(dma_q, &buf); + if (!buf) + outp = videobuf_to_vmalloc(&buf->vb); + + if (!outp) + return 0; + + size = buf->vb.size; + + copied=0; + + if (urb->status<0) { + print_err_status (dev,-1,urb->status); + return 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int status = urb->iso_frame_desc[i].status; + + if (status<0) { + print_err_status (dev,i,status); + continue; + } + + len=urb->iso_frame_desc[i].actual_length; + +// if (len>=TM6000_URB_MSG_LEN) { + p=urb->transfer_buffer + urb->iso_frame_desc[i].offset; + if (!urb->iso_frame_desc[i].status) { + if ((buf->fmt->fourcc)==V4L2_PIX_FMT_TM6000) { + rc=copy_multiplexed(p, outp, len, urb, &buf); + if (rc<=0) + return rc; + } else { + copy_streams(p, outp, len, urb, &buf); + } + } + copied += len; + if (copied>=size) + break; +// } + } + return rc; +} + +/* ------------------------------------------------------------------ + URB control + ------------------------------------------------------------------*/ + +/* + * IRQ callback, called by URB callback + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 19) +static void tm6000_irq_callback(struct urb *urb, struct pt_regs *regs) +#else +static void tm6000_irq_callback(struct urb *urb) +#endif +{ + struct tm6000_dmaqueue *dma_q = urb->context; + struct tm6000_core *dev = container_of(dma_q, struct tm6000_core, vidq); + int i; + + if (!dev) + return; + + spin_lock(&dev->slock); + tm6000_isoc_copy(urb); + spin_unlock(&dev->slock); + + /* Reset urb buffers */ + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + + urb->status = usb_submit_urb(urb, GFP_ATOMIC); + if (urb->status) + tm6000_err("urb resubmit failed (error=%i)\n", + urb->status); +} + +/* + * Stop and Deallocate URBs + */ +static void tm6000_uninit_isoc(struct tm6000_core *dev) +{ + struct urb *urb; + int i; + + dev->isoc_ctl.nfields = -1; + dev->isoc_ctl.buf = NULL; + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb=dev->isoc_ctl.urb[i]; + if (urb) { + usb_kill_urb(urb); + usb_unlink_urb(urb); + if (dev->isoc_ctl.transfer_buffer[i]) { + usb_buffer_free(dev->udev, + urb->transfer_buffer_length, + dev->isoc_ctl.transfer_buffer[i], + urb->transfer_dma); + } + usb_free_urb(urb); + dev->isoc_ctl.urb[i] = NULL; + } + dev->isoc_ctl.transfer_buffer[i] = NULL; + } + + kfree (dev->isoc_ctl.urb); + kfree (dev->isoc_ctl.transfer_buffer); + + dev->isoc_ctl.urb=NULL; + dev->isoc_ctl.transfer_buffer=NULL; + dev->isoc_ctl.num_bufs = 0; + + dev->isoc_ctl.num_bufs=0; +} + +/* + * Allocate URBs and start IRQ + */ +static int tm6000_prepare_isoc(struct tm6000_core *dev, unsigned int framesize) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i, j, sb_size, pipe, size, max_packets, num_bufs = 5; + struct urb *urb; + + /* De-allocates all pending stuff */ + tm6000_uninit_isoc(dev); + + pipe = usb_rcvisocpipe(dev->udev, + dev->isoc_in->desc.bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + + size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe)); + + if (size > dev->max_isoc_in) + size = dev->max_isoc_in; + + dev->isoc_ctl.max_pkt_size = size; + + max_packets = ( framesize + size - 1) / size; + + if (max_packets > TM6000_MAX_ISO_PACKETS) + max_packets = TM6000_MAX_ISO_PACKETS; + + sb_size = max_packets * size; + + dev->isoc_ctl.num_bufs = num_bufs; + + dev->isoc_ctl.urb = kmalloc(sizeof(void *)*num_bufs, GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + tm6000_err("cannot alloc memory for usb buffers\n"); + return -ENOMEM; + } + + dev->isoc_ctl.transfer_buffer = kmalloc(sizeof(void *)*num_bufs, + GFP_KERNEL); + if (!dev->isoc_ctl.urb) { + tm6000_err("cannot allocate memory for usbtransfer\n"); + kfree(dev->isoc_ctl.urb); + return -ENOMEM; + } + + dprintk(dev, V4L2_DEBUG_QUEUE, "Allocating %d x %d packets" + " (%d bytes) of %d bytes each to handle %u size\n", + max_packets, num_bufs, sb_size, + dev->max_isoc_in, size); + +#if 0 + /* reset streaming vars */ + dev->isoc_ctl.frame_current = NULL; + dev->isoc_ctl.frame_count = 0; +#endif + + /* allocate urbs and transfer buffers */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + urb = usb_alloc_urb(max_packets, GFP_KERNEL); + if (!urb) { + tm6000_err("cannot alloc isoc_ctl.urb %i\n", i); + tm6000_uninit_isoc(dev); + usb_free_urb(urb); + return -ENOMEM; + } + dev->isoc_ctl.urb[i] = urb; + + dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->udev, + sb_size, GFP_KERNEL, &urb->transfer_dma); + if (!dev->isoc_ctl.transfer_buffer[i]) { + tm6000_err ("unable to allocate %i bytes for transfer" + " buffer %i%s\n", + sb_size, i, + in_interrupt()?" while in int":""); + tm6000_uninit_isoc(dev); + return -ENOMEM; + } + memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size); + + usb_fill_bulk_urb(urb, dev->udev, pipe, + dev->isoc_ctl.transfer_buffer[i], sb_size, + tm6000_irq_callback, dma_q); + urb->interval = dev->isoc_in->desc.bInterval; + urb->number_of_packets = max_packets; + urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; + + for (j = 0; j < max_packets; j++) { + urb->iso_frame_desc[j].offset = size * j; + urb->iso_frame_desc[j].length = size; + } + } + + return 0; +} + +static int tm6000_start_thread( struct tm6000_core *dev) +{ + struct tm6000_dmaqueue *dma_q = &dev->vidq; + int i; + + dma_q->frame=0; + dma_q->ini_jiffies=jiffies; + + init_waitqueue_head(&dma_q->wq); + + /* submit urbs and enables IRQ */ + for (i = 0; i < dev->isoc_ctl.num_bufs; i++) { + int rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC); + if (rc) { + tm6000_err("submit of urb %i failed (error=%i)\n", i, + rc); + tm6000_uninit_isoc(dev); + return rc; + } + } + + return 0; +} + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int +buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) +{ + struct tm6000_fh *fh = vq->priv_data; + + *size = fh->fmt->depth * fh->width * fh->height >> 3; + if (0 == *count) + *count = TM6000_DEF_BUF; + + if (*count < TM6000_MIN_BUF) { + *count=TM6000_MIN_BUF; + } + + while (*size * *count > vid_limit * 1024 * 1024) + (*count)--; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct tm6000_buffer *buf) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + unsigned long flags; + + if (in_interrupt()) + BUG(); + + /* We used to wait for the buffer to finish here, but this didn't work + because, as we were keeping the state as VIDEOBUF_QUEUED, + videobuf_queue_cancel marked it as finished for us. + (Also, it could wedge forever if the hardware was misconfigured.) + + This should be safe; by the time we get here, the buffer isn't + queued anymore. If we ever start marking the buffers as + VIDEOBUF_ACTIVE, it won't be, though. + */ + spin_lock_irqsave(&dev->slock, flags); + if (dev->isoc_ctl.buf == buf) + dev->isoc_ctl.buf = NULL; + spin_unlock_irqrestore(&dev->slock, flags); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int +buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); + struct tm6000_core *dev = fh->dev; + int rc = 0, urb_init = 0; + + BUG_ON(NULL == fh->fmt); + +#if 0 + if (fh->width < norm_minw(core) || fh->width > norm_maxw(core) || + fh->height < norm_minh(core) || fh->height > norm_maxh(core)) { + dprintk(dev, V4L2_DEBUG_QUEUE, "Window size (%dx%d) is out of " + "supported range\n", fh->width, fh->height); + dprintk(dev, V4L2_DEBUG_QUEUE, "Valid range is from (%dx%d) to " + "(%dx%d)\n", norm_minw(core), norm_minh(core), + norm_maxw(core),norm_maxh(core)); + return -EINVAL; + } +#endif + + /* FIXME: It assumes depth=2 */ + /* The only currently supported format is 16 bits/pixel */ + buf->vb.size = fh->fmt->depth*fh->width*fh->height >> 3; + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) + return -EINVAL; + + if (buf->fmt != fh->fmt || + buf->vb.width != fh->width || + buf->vb.height != fh->height || + buf->vb.field != field) { + buf->fmt = fh->fmt; + buf->vb.width = fh->width; + buf->vb.height = fh->height; + buf->vb.field = field; + buf->vb.state = VIDEOBUF_NEEDS_INIT; + } + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + if (0 != (rc = videobuf_iolock(vq, &buf->vb, NULL))) + goto fail; + urb_init = 1; + } + + if (!dev->isoc_ctl.num_bufs) + urb_init = 1; + + if (urb_init) { + rc = tm6000_prepare_isoc(dev, buf->vb.size); + if (rc < 0) + goto fail; + + rc = tm6000_start_thread(dev); + if (rc < 0) + goto fail; + + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; + +fail: + free_buffer(vq, buf); + return rc; +} + +static void +buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); + struct tm6000_fh *fh = vq->priv_data; + struct tm6000_core *dev = fh->dev; + struct tm6000_dmaqueue *vidq = &dev->vidq; + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct tm6000_buffer *buf = container_of(vb,struct tm6000_buffer,vb); + + free_buffer(vq,buf); +} + +static struct videobuf_queue_ops tm6000_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* ------------------------------------------------------------------ + IOCTL handling + ------------------------------------------------------------------*/ + +static int res_get(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + /* is it free? */ + mutex_lock(&dev->lock); + if (dev->resources) { + /* no, someone else uses it */ + mutex_unlock(&dev->lock); + return 0; + } + /* it's free, grab it */ + dev->resources =1; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: get\n"); + mutex_unlock(&dev->lock); + return 1; +} + +static int res_locked(struct tm6000_core *dev) +{ + return (dev->resources); +} + +static void res_free(struct tm6000_core *dev, struct tm6000_fh *fh) +{ + mutex_lock(&dev->lock); + dev->resources = 0; + dprintk(dev, V4L2_DEBUG_RES_LOCK, "res: put\n"); + mutex_unlock(&dev->lock); +} + +/* ------------------------------------------------------------------ + IOCTL vidioc handling + ------------------------------------------------------------------*/ +static int vidioc_querycap (struct file *file, void *priv, + struct v4l2_capability *cap) +{ + // struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + + strlcpy(cap->driver, "tm6000", sizeof(cap->driver)); + strlcpy(cap->card,"Trident TVMaster TM5600/6000", sizeof(cap->card)); + // strlcpy(cap->bus_info, dev->udev->dev.bus_id, sizeof(cap->bus_info)); + cap->version = TM6000_VERSION; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_STREAMING | + V4L2_CAP_TUNER | + V4L2_CAP_READWRITE; + return 0; +} + +static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + if (unlikely(f->index >= ARRAY_SIZE(format))) + return -EINVAL; + + strlcpy(f->description,format[f->index].name,sizeof(f->description)); + f->pixelformat = format[f->index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh=priv; + + f->fmt.pix.width = fh->width; + f->fmt.pix.height = fh->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = fh->fmt->fourcc; + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fh->fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return (0); +} + +static struct tm6000_fmt* format_by_fourcc(unsigned int fourcc) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(format); i++) + if (format[i].fourcc == fourcc) + return format+i; + return NULL; +} + +static int vidioc_try_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_core *dev = ((struct tm6000_fh *)priv)->dev; + struct tm6000_fmt *fmt; + enum v4l2_field field; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + if (NULL == fmt) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Fourcc format (0x%08x)" + " invalid.\n", f->fmt.pix.pixelformat); + return -EINVAL; + } + + field = f->fmt.pix.field; + + if (field == V4L2_FIELD_ANY) { +// field=V4L2_FIELD_INTERLACED; + field=V4L2_FIELD_SEQ_TB; + } else if (V4L2_FIELD_INTERLACED != field) { + dprintk(dev, V4L2_DEBUG_IOCTL_ARG, "Field type invalid.\n"); + return -EINVAL; + } + +#if 1 + tm6000_get_std_res (dev); + + f->fmt.pix.width = dev->width; + f->fmt.pix.height = dev->height; +#else + /* Scaling is not yet supported */ + if (f->fmt.pix.width < norm_minw(core)) + f->fmt.pix.width = norm_minw(core); + + if (f->fmt.pix.width > norm_maxw(core)) + f->fmt.pix.width = norm_maxw(core); + + if (f->fmt.pix.height < norm_minh(core)) + f->fmt.pix.height = norm_minh(core); + + if (f->fmt.pix.height > norm_maxh(core)) + f->fmt.pix.height = norm_maxh(core); +#endif + + f->fmt.pix.width &= ~0x01; + + f->fmt.pix.field = field; + + f->fmt.pix.bytesperline = + (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = + f->fmt.pix.height * f->fmt.pix.bytesperline; + + return 0; +} + +/*FIXME: This seems to be generic enough to be at videodev2 */ +static int vidioc_s_fmt_vid_cap (struct file *file, void *priv, + struct v4l2_format *f) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + int ret = vidioc_try_fmt_vid_cap(file,fh,f); + if (ret < 0) + return (ret); + + fh->fmt = format_by_fourcc(f->fmt.pix.pixelformat); + fh->width = f->fmt.pix.width; + fh->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + + dev->fourcc = f->fmt.pix.pixelformat; + + tm6000_set_fourcc_format(dev); + + return (0); +} + +static int vidioc_reqbufs (struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_reqbufs(&fh->vb_vidq, p)); +} + +static int vidioc_querybuf (struct file *file, void *priv, + struct v4l2_buffer *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_querybuf(&fh->vb_vidq, p)); +} + +static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_qbuf(&fh->vb_vidq, p)); +} + +static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *p) +{ + struct tm6000_fh *fh=priv; + + return (videobuf_dqbuf(&fh->vb_vidq, p, + file->f_flags & O_NONBLOCK)); +} + +#ifdef CONFIG_VIDEO_V4L1_COMPAT +static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf) +{ + struct tm6000_fh *fh=priv; + + return videobuf_cgmbuf (&fh->vb_vidq, mbuf, 8); +} +#endif + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + if (!res_get(dev,fh)) + return -EBUSY; + return (videobuf_streamon(&fh->vb_vidq)); +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (i != fh->type) + return -EINVAL; + + videobuf_streamoff(&fh->vb_vidq); + res_free(dev,fh); + + return (0); +} + +static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *norm) +{ + int rc=0; + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + rc=tm6000_set_standard (dev, norm); + +#if 1 + fh->width = dev->width; + fh->height = dev->height; +#endif + + if (rc<0) + return rc; + + v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_std, dev->norm); + + return 0; +} + +static int vidioc_enum_input (struct file *file, void *priv, + struct v4l2_input *inp) +{ + switch (inp->index) { + case TM6000_INPUT_TV: + inp->type = V4L2_INPUT_TYPE_TUNER; + strcpy(inp->name,"Television"); + break; + case TM6000_INPUT_COMPOSITE: + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name,"Composite"); + break; + case TM6000_INPUT_SVIDEO: + inp->type = V4L2_INPUT_TYPE_CAMERA; + strcpy(inp->name,"S-Video"); + break; + default: + return -EINVAL; + } + inp->std = TM6000_STD; + + return 0; +} + +static int vidioc_g_input (struct file *file, void *priv, unsigned int *i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + + *i=dev->input; + + return 0; +} +static int vidioc_s_input (struct file *file, void *priv, unsigned int i) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + int rc=0; + char buf[1]; + + switch (i) { + case TM6000_INPUT_TV: + dev->input=i; + *buf=0; + break; + case TM6000_INPUT_COMPOSITE: + case TM6000_INPUT_SVIDEO: + dev->input=i; + *buf=1; + break; + default: + return -EINVAL; + } + rc=tm6000_read_write_usb (dev, USB_DIR_OUT | USB_TYPE_VENDOR, + REQ_03_SET_GET_MCU_PIN, 0x03, 1, buf, 1); + + if (!rc) { + dev->input=i; + rc=vidioc_s_std (file, priv, &dev->vfd->current_norm); + } + + return (rc); +} + + /* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl (struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + if (qc->id && qc->id == tm6000_qctrl[i].id) { + memcpy(qc, &(tm6000_qctrl[i]), + sizeof(*qc)); + return (0); + } + + return -EINVAL; +} + +static int vidioc_g_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh=priv; + struct tm6000_core *dev = fh->dev; + int val; + + /* FIXME: Probably, those won't work! Maybe we need shadow regs */ + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x08, 0); + break; + case V4L2_CID_BRIGHTNESS: + val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x09, 0); + return 0; + case V4L2_CID_SATURATION: + val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x0a, 0); + return 0; + case V4L2_CID_HUE: + val=tm6000_get_reg (dev, REQ_07_SET_GET_AVREG, 0x0b, 0); + return 0; + default: + return -EINVAL; + } + + if (val<0) + return val; + + ctrl->value=val; + + return 0; +} +static int vidioc_s_ctrl (struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + u8 val=ctrl->value; + + switch (ctrl->id) { + case V4L2_CID_CONTRAST: + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x08, val); + return 0; + case V4L2_CID_BRIGHTNESS: + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x09, val); + return 0; + case V4L2_CID_SATURATION: + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0a, val); + return 0; + case V4L2_CID_HUE: + tm6000_set_reg (dev, REQ_07_SET_GET_AVREG, 0x0b, val); + return 0; + } + return -EINVAL; +} + +static int vidioc_g_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + strcpy(t->name, "Television"); + t->type = V4L2_TUNER_ANALOG_TV; + t->capability = V4L2_TUNER_CAP_NORM; + t->rangehigh = 0xffffffffUL; + t->rxsubchans = V4L2_TUNER_SUB_MONO; + + return 0; +} + +static int vidioc_s_tuner (struct file *file, void *priv, + struct v4l2_tuner *t) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (UNSET == dev->tuner_type) + return -EINVAL; + if (0 != t->index) + return -EINVAL; + + return 0; +} + +static int vidioc_g_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + + f->type = V4L2_TUNER_ANALOG_TV; + f->frequency = dev->freq; + + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_frequency, f); + + return 0; +} + +static int vidioc_s_frequency (struct file *file, void *priv, + struct v4l2_frequency *f) +{ + struct tm6000_fh *fh =priv; + struct tm6000_core *dev = fh->dev; + + if (unlikely(f->type != V4L2_TUNER_ANALOG_TV)) + return -EINVAL; + + if (unlikely(UNSET == dev->tuner_type)) + return -EINVAL; + if (unlikely(f->tuner != 0)) + return -EINVAL; + +// mutex_lock(&dev->lock); + dev->freq = f->frequency; + v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, f); +// mutex_unlock(&dev->lock); + + return 0; +} + +/* ------------------------------------------------------------------ + File operations for the device + ------------------------------------------------------------------*/ + +static int tm6000_open(struct file *file) +{ + int minor = video_devdata(file)->minor; + struct tm6000_core *h,*dev = NULL; + struct tm6000_fh *fh; + struct list_head *list; + enum v4l2_buf_type type = 0; + int i,rc; + + printk(KERN_INFO "tm6000: open called (minor=%d)\n",minor); + + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: open called " + "(minor=%d)\n",minor); + + list_for_each(list,&tm6000_corelist) { + h = list_entry(list, struct tm6000_core, tm6000_corelist); + if (h->vfd->minor == minor) { + dev = h; + type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + } + } + if (NULL == dev) + return -ENODEV; + +#if 0 /* Avoids an oops at read() - seems to be semaphore related */ + if (dev->users) { + printk(KERN_INFO "this driver can be opened only once (users=%d)\n",dev->users); + return -EBUSY; + } +#endif + + /* If more than one user, mutex should be added */ + dev->users++; + + dprintk(dev, V4L2_DEBUG_OPEN, "open minor=%d type=%s users=%d\n", + minor,v4l2_type_names[type],dev->users); + + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh),GFP_KERNEL); + if (NULL == fh) { + dev->users--; + return -ENOMEM; + } + + file->private_data = fh; + fh->dev = dev; + + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + dev->fourcc = format[0].fourcc; + + fh->fmt = format_by_fourcc(dev->fourcc); + + tm6000_get_std_res (dev); + + fh->width = dev->width; + fh->height = dev->height; + + dprintk(dev, V4L2_DEBUG_OPEN, "Open: fh=0x%08lx, dev=0x%08lx, " + "dev->vidq=0x%08lx\n", + (unsigned long)fh,(unsigned long)dev,(unsigned long)&dev->vidq); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "queued=%d\n",list_empty(&dev->vidq.queued)); + dprintk(dev, V4L2_DEBUG_OPEN, "Open: list_empty " + "active=%d\n",list_empty(&dev->vidq.active)); + +#if 0 + /* Resets frame counters */ + sprintf(dev->timestr,"%02d:%02d:%02d:%03d", + dev->h,dev->m,dev->s,(dev->us+500)/1000); +#endif + /* initialize hardware on analog mode */ + if (dev->mode!=TM6000_MODE_ANALOG) { + rc=tm6000_init_analog_mode (dev); + if (rc<0) + return rc; + + /* Put all controls at a sane state */ + for (i = 0; i < ARRAY_SIZE(tm6000_qctrl); i++) + qctl_regs[i] =tm6000_qctrl[i].default_value; + + dev->mode=TM6000_MODE_ANALOG; + } + + videobuf_queue_vmalloc_init(&fh->vb_vidq, &tm6000_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct tm6000_buffer),fh); + + return 0; +} + +static ssize_t +tm6000_read(struct file *file, char __user *data, size_t count, loff_t *pos) +{ + struct tm6000_fh *fh = file->private_data; + + if (fh->type==V4L2_BUF_TYPE_VIDEO_CAPTURE) { + if (res_locked(fh->dev)) + return -EBUSY; + + return videobuf_read_stream(&fh->vb_vidq, data, count, pos, 0, + file->f_flags & O_NONBLOCK); +#if 0 + return videobuf_read_one(&fh->vb_vidq, data, count, pos, + file->f_flags & O_NONBLOCK); +#endif + } + return 0; +} + +static unsigned int +tm6000_poll(struct file *file, struct poll_table_struct *wait) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_buffer *buf; + + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + + if (res_get(fh->dev,fh)) { + /* streaming capture */ + if (list_empty(&fh->vb_vidq.stream)) + return POLLERR; + buf = list_entry(fh->vb_vidq.stream.next,struct tm6000_buffer,vb.stream); + } else { + /* read() capture */ +#if 0 + buf = (struct tm6000_buffer*)fh->vb_vidq.read_buf; + if (NULL == buf) + return POLLERR; +#else + return videobuf_poll_stream(file, &fh->vb_vidq, + wait); +#endif + } + poll_wait(file, &buf->vb.done, wait); + if (buf->vb.state == VIDEOBUF_DONE || + buf->vb.state == VIDEOBUF_ERROR) + return POLLIN|POLLRDNORM; + return 0; +} + +static int tm6000_release(struct file *file) +{ + struct tm6000_fh *fh = file->private_data; + struct tm6000_core *dev = fh->dev; + int minor = video_devdata(file)->minor; + + dprintk(dev, V4L2_DEBUG_OPEN, "tm6000: close called (minor=%d, users=%d)\n",minor,dev->users); + + dev->users--; + + if (!dev->users) { + tm6000_uninit_isoc(dev); + videobuf_mmap_free(&fh->vb_vidq); + } + + kfree (fh); + + return 0; +} + +static int tm6000_mmap(struct file *file, struct vm_area_struct * vma) +{ + struct tm6000_fh *fh = file->private_data; + int ret; + + ret=videobuf_mmap_mapper(&fh->vb_vidq, vma); + + return ret; +} + +static struct v4l2_file_operations tm6000_fops = { + .owner = THIS_MODULE, + .open = tm6000_open, + .release = tm6000_release, + .ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .read = tm6000_read, + .poll = tm6000_poll, + .mmap = tm6000_mmap, +}; + +static const struct v4l2_ioctl_ops video_ioctl_ops = { + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_g_tuner = vidioc_g_tuner, + .vidioc_s_tuner = vidioc_s_tuner, + .vidioc_g_frequency = vidioc_g_frequency, + .vidioc_s_frequency = vidioc_s_frequency, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, +#ifdef CONFIG_VIDEO_V4L1_COMPAT + .vidiocgmbuf = vidiocgmbuf, +#endif +}; + +static struct video_device tm6000_template = { + .name = "tm6000", + .fops = &tm6000_fops, + .ioctl_ops = &video_ioctl_ops, + .minor = -1, + .release = video_device_release, + .tvnorms = TM6000_STD, + .current_norm = V4L2_STD_NTSC_M, +}; + +/* ----------------------------------------------------------------- + Initialization and module stuff + ------------------------------------------------------------------*/ + +int tm6000_v4l2_register(struct tm6000_core *dev) +{ + int ret = -1; + struct video_device *vfd; + + vfd = video_device_alloc(); + if(!vfd) { + return -ENOMEM; + } + dev->vfd = vfd; + + list_add_tail(&dev->tm6000_corelist,&tm6000_corelist); + + /* init video dma queues */ + INIT_LIST_HEAD(&dev->vidq.active); + INIT_LIST_HEAD(&dev->vidq.queued); + + memcpy (dev->vfd, &tm6000_template, sizeof(*(dev->vfd))); + dev->vfd->debug=tm6000_debug; + vfd->v4l2_dev = &dev->v4l2_dev; + + ret = video_register_device(dev->vfd, VFL_TYPE_GRABBER, video_nr); + printk(KERN_INFO "Trident TVMaster TM5600/TM6000 USB2 board (Load status: %d)\n", ret); + return ret; +} + +int tm6000_v4l2_unregister(struct tm6000_core *dev) +{ + struct tm6000_core *h; + struct list_head *pos, *tmp; + + video_unregister_device(dev->vfd); + + list_for_each_safe(pos, tmp, &tm6000_corelist) { + h = list_entry(pos, struct tm6000_core, tm6000_corelist); + if (h == dev) { + list_del(pos); + } + } + + return 0; +} + +int tm6000_v4l2_exit(void) +{ +#if 0 + struct tm6000_core *h; + struct list_head *list; + + while (!list_empty(&tm6000_corelist)) { + list = tm6000_corelist.next; + list_del(list); + h = list_entry(list, struct tm6000_core, tm6000_corelist); + video_unregister_device(&h->vfd); + kfree (h); + } +#endif + return 0; +} + +module_param(video_nr, int, 0); +MODULE_PARM_DESC(video_nr,"Allow changing video device number"); + +module_param_named (debug, tm6000_debug, int, 0444); +MODULE_PARM_DESC(debug,"activates debug info"); + +module_param(vid_limit,int,0644); +MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); + diff --git a/linux/drivers/staging/tm6000/tm6000.h b/linux/drivers/staging/tm6000/tm6000.h new file mode 100644 index 000000000..ba0acd2a6 --- /dev/null +++ b/linux/drivers/staging/tm6000/tm6000.h @@ -0,0 +1,294 @@ +/* + tm6000.h - driver for TM5600/TM6000 USB video capture devices + + Copyright (C) 2006-2007 Mauro Carvalho Chehab <mchehab@infradead.org> + + Copyright (C) 2007 Michel Ludwig <michel.ludwig@gmail.com> + - DVB-T support + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation version 2 + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +// Use the tm6000-hack, instead of the proper initialization code +//#define HACK 1 + +#include "compat.h" +#include <linux/videodev2.h> +#include <media/v4l2-common.h> +#include <media/videobuf-vmalloc.h> +#include "tm6000-usb-isoc.h" +#include <linux/i2c.h> +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) +#include <linux/mutex.h> +#endif +#include <media/v4l2-device.h> + + +#include <linux/dvb/frontend.h> +#include "dvb_demux.h" +#include "dvb_frontend.h" +#include "dmxdev.h" + +#define TM6000_VERSION KERNEL_VERSION(0, 0, 1) + +/* Inputs */ + +enum tm6000_itype { + TM6000_INPUT_TV = 0, + TM6000_INPUT_COMPOSITE, + TM6000_INPUT_SVIDEO, +}; + +enum tm6000_devtype { + TM6000 = 0, + TM5600, + TM6010, +}; + +/* ------------------------------------------------------------------ + Basic structures + ------------------------------------------------------------------*/ + +struct tm6000_fmt { + char *name; + u32 fourcc; /* v4l2 format id */ + int depth; +}; + +/* buffer for one video frame */ +struct tm6000_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + + struct tm6000_fmt *fmt; +}; + +struct tm6000_dmaqueue { + struct list_head active; + struct list_head queued; + + /* thread for generating video stream*/ + struct task_struct *kthread; + wait_queue_head_t wq; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) + struct semaphore *notify; + int rmmod:1; +#endif + /* Counters to control fps rate */ + int frame; + int ini_jiffies; +}; + +/* device states */ +enum tm6000_core_state { + DEV_INITIALIZED = 0x01, + DEV_DISCONNECTED = 0x02, + DEV_MISCONFIGURED = 0x04, +}; + +#if 1 +/* io methods */ +enum tm6000_io_method { + IO_NONE, + IO_READ, + IO_MMAP, +}; +#endif + +enum tm6000_mode { + TM6000_MODE_UNKNOWN=0, + TM6000_MODE_ANALOG, + TM6000_MODE_DIGITAL, +}; + +struct tm6000_capabilities { + unsigned int has_tuner:1; + unsigned int has_tda9874:1; + unsigned int has_dvb:1; + unsigned int has_zl10353:1; + unsigned int has_eeprom:1; + unsigned int has_remote:1; +}; + +struct tm6000_dvb { + struct dvb_adapter adapter; + struct dvb_demux demux; + struct dvb_frontend *frontend; + struct dmxdev dmxdev; + unsigned int streams; + struct urb *bulk_urb; + struct mutex mutex; +}; + +struct tm6000_core { + /* generic device properties */ + char name[30]; /* name (including minor) of the device */ + int model; /* index in the device_data struct */ + int devno; /* marks the number of this device */ + enum tm6000_devtype dev_type; /* type of device */ + + v4l2_std_id norm; /* Current norm */ + int width,height; /* Selected resolution */ + + enum tm6000_core_state state; + + /* Device Capabilities*/ + struct tm6000_capabilities caps; + + /* Tuner configuration */ + int tuner_type; /* type of the tuner */ + int tuner_addr; /* tuner address */ + int tuner_reset_gpio; /* GPIO used for tuner reset */ + + /* Demodulator configuration */ + int demod_addr; /* demodulator address */ + + int audio_bitrate; + /* i2c i/o */ + struct i2c_adapter i2c_adap; + struct i2c_client i2c_client; + + /* video for linux */ + struct list_head tm6000_corelist; + int users; + + /* various device info */ + unsigned int resources; + struct video_device *vfd; + struct tm6000_dmaqueue vidq; + struct v4l2_device v4l2_dev; + + int input; + int freq; + unsigned int fourcc; + + enum tm6000_mode mode; + + /* DVB-T support */ + struct tm6000_dvb *dvb; + + /* locks */ + struct mutex lock; + + /* usb transfer */ + struct usb_device *udev; /* the usb device */ + + struct usb_host_endpoint *bulk_in, *bulk_out, *isoc_in, *isoc_out; + unsigned int max_bulk_in, max_bulk_out; + unsigned int max_isoc_in, max_isoc_out; + + /* scaler!=0 if scaler is active*/ + int scaler; + + /* Isoc control struct */ + struct usb_isoc_ctl isoc_ctl; + + spinlock_t slock; +}; + +struct tm6000_fh { + struct tm6000_core *dev; + + /* video capture */ + struct tm6000_fmt *fmt; + unsigned int width,height; + struct videobuf_queue vb_vidq; + + enum v4l2_buf_type type; +}; + +#define TM6000_STD V4L2_STD_PAL|V4L2_STD_PAL_N|V4L2_STD_PAL_Nc| \ + V4L2_STD_PAL_M|V4L2_STD_PAL_60|V4L2_STD_NTSC_M| \ + V4L2_STD_NTSC_M_JP|V4L2_STD_SECAM + +/* In tm6000-core.c */ + +int tm6000_read_write_usb (struct tm6000_core *dev, u8 reqtype, u8 req, + u16 value, u16 index, u8 *buf, u16 len); +int tm6000_get_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_set_reg (struct tm6000_core *dev, u8 req, u16 value, u16 index); +int tm6000_init (struct tm6000_core *dev); +int tm6000_init_after_firmware (struct tm6000_core *dev); + +int tm6000_init_analog_mode (struct tm6000_core *dev); +int tm6000_init_digital_mode (struct tm6000_core *dev); +int tm6000_set_audio_bitrate (struct tm6000_core *dev, int bitrate); + +int tm6000_dvb_register(struct tm6000_core *dev); +void tm6000_dvb_unregister(struct tm6000_core *dev); + +int tm6000_v4l2_register(struct tm6000_core *dev); +int tm6000_v4l2_unregister(struct tm6000_core *dev); +int tm6000_v4l2_exit(void); +void tm6000_set_fourcc_format(struct tm6000_core *dev); + +/* In tm6000-stds.c */ +void tm6000_get_std_res(struct tm6000_core *dev); +int tm6000_set_standard (struct tm6000_core *dev, v4l2_std_id *norm); + +/* In tm6000-i2c.c */ +int tm6000_i2c_register(struct tm6000_core *dev); +int tm6000_i2c_unregister(struct tm6000_core *dev); + +#if 1 +/* In tm6000-queue.c */ +#if 0 +int tm6000_init_isoc(struct tm6000_core *dev, int max_packets); +void tm6000_uninit_isoc(struct tm6000_core *dev); +#endif + +int tm6000_v4l2_mmap(struct file *filp, struct vm_area_struct *vma); + +int tm6000_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type i); +int tm6000_vidioc_reqbufs (struct file *file, void *priv, + struct v4l2_requestbuffers *rb); +int tm6000_vidioc_querybuf (struct file *file, void *priv, + struct v4l2_buffer *b); +int tm6000_vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *b); +int tm6000_vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *b); +ssize_t tm6000_v4l2_read(struct file *filp, char __user * buf, size_t count, + loff_t * f_pos); +unsigned int tm6000_v4l2_poll(struct file *file, + struct poll_table_struct *wait); +int tm6000_queue_init(struct tm6000_core *dev); + +/* In tm6000-alsa.c */ +int tm6000_audio_init(struct tm6000_core *dev, int idx); + + +/* Debug stuff */ + +extern int tm6000_debug; + +#define dprintk(dev, level, fmt, arg...) do {\ + if (tm6000_debug & level) \ + printk(KERN_INFO "(%lu) %s %s :"fmt, jiffies, \ + dev->name, __FUNCTION__ , ##arg); } while (0) + +#define V4L2_DEBUG_REG 0x0004 +#define V4L2_DEBUG_I2C 0x0008 +#define V4L2_DEBUG_QUEUE 0x0010 +#define V4L2_DEBUG_ISOC 0x0020 +#define V4L2_DEBUG_RES_LOCK 0x0040 /* Resource locking */ +#define V4L2_DEBUG_OPEN 0x0080 /* video open/close debug */ + +#define tm6000_err(fmt, arg...) do {\ + printk(KERN_ERR "tm6000 %s :"fmt, \ + __FUNCTION__ , ##arg); } while (0) + + +#endif diff --git a/linux/include/linux/videodev2.h b/linux/include/linux/videodev2.h index 9d61bae27..b8fb6c206 100644 --- a/linux/include/linux/videodev2.h +++ b/linux/include/linux/videodev2.h @@ -361,6 +361,7 @@ struct v4l2_pix_format { #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */ #define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */ +#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */ /* * F O R M A T E N U M E R A T I O N diff --git a/linux/include/media/v4l2-dev.h b/linux/include/media/v4l2-dev.h index 1cae97561..f813ae9c0 100644 --- a/linux/include/media/v4l2-dev.h +++ b/linux/include/media/v4l2-dev.h @@ -105,6 +105,10 @@ struct video_device Also note that vdev->minor is set to -1 if the registration failed. */ int __must_check video_register_device(struct video_device *vdev, int type, int nr); +/* Same as video_register_device, but no warning is issued if the desired + device node number was already in use. */ +int __must_check video_register_device_no_warn(struct video_device *vdev, int type, int nr); + /* Unregister video devices. Will do nothing if vdev == NULL or vdev->minor < 0. */ void video_unregister_device(struct video_device *vdev); diff --git a/linux/linux/drivers/staging/tm6000/tm6000-alsa.c b/linux/linux/drivers/staging/tm6000/tm6000-alsa.c new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/linux/linux/drivers/staging/tm6000/tm6000-alsa.c |