Merge git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb
* git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/v4l-dvb: (28 commits) V4L-DVB(7789a): cx18: fix symbol conflict with ivtv driver V4L/DVB (7789): tuner: remove static dependencies on analog tuner sub-modules V4L/DVB (7785): [2.6 patch] make mt9{m001,v022}_controls[] static V4L/DVB (7786): cx18: new driver for the Conexant CX23418 MPEG encoder chip V4L/DVB (7783): drivers/media/dvb/frontends/s5h1420.c: printk fix V4L/DVB (7782): pvrusb2: Driver is no longer experimental V4L/DVB (7781): pvrusb2-dvb: include dvb support by default and update Kconfig help text V4L/DVB (7780): pvrusb2: always enable support for OnAir Creator / HDTV USB2 V4L/DVB (7779): pvrusb2-dvb: quiet down noise in kernel log for feed debug Rename common tuner Kconfig names to use the same Fix V4L/DVB core help messages V4L/DVB (7769): Move other terrestrial tuners to common/tuners V4L/DVB (7768): reorganize some DVB-S Kconfig items V4L/DVB(7767): Move tuners to common/tuners V4L/DVB (7766): saa7134: add another PCI ID for Beholder M6 V4L/DVB (7765): Add support for Beholder BeholdTV H6 V4L/DVB (7763): ivtv: add tuner support for the AverMedia M116 V4L/DVB (7762): ivtv: fix tuner detection for PAL-N/Nc V4L/DVB (7761): ivtv: increase the DMA timeout from 100 to 300 ms V4L/DVB (7759): ivtv: increase version number to 1.2.1 ...
This commit is contained in:
Коммит
2d5e3e8d28
|
@ -128,7 +128,7 @@
|
|||
127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090]
|
||||
128 -> Beholder BeholdTV Columbus TVFM [0000:5201]
|
||||
129 -> Beholder BeholdTV 607 / BeholdTV 609 [5ace:6070,5ace:6071,5ace:6072,5ace:6073,5ace:6090,5ace:6091,5ace:6092,5ace:6093]
|
||||
130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193]
|
||||
130 -> Beholder BeholdTV M6 / BeholdTV M6 Extra [5ace:6190,5ace:6193,5ace:6191]
|
||||
131 -> Twinhan Hybrid DTV-DVB 3056 PCI [1822:0022]
|
||||
132 -> Genius TVGO AM11MCE
|
||||
133 -> NXP Snake DVB-S reference design
|
||||
|
@ -140,3 +140,4 @@
|
|||
139 -> Compro VideoMate T750 [185b:c900]
|
||||
140 -> Avermedia DVB-S Pro A700 [1461:a7a1]
|
||||
141 -> Avermedia DVB-S Hybrid+FM A700 [1461:a7a2]
|
||||
142 -> Beholder BeholdTV H6 [5ace:6290]
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
Some notes regarding the cx18 driver for the Conexant CX23418 MPEG
|
||||
encoder chip:
|
||||
|
||||
1) The only hardware currently supported is the Hauppauge HVR-1600.
|
||||
|
||||
2) Some people have problems getting the i2c bus to work. Cause unknown.
|
||||
The symptom is that the eeprom cannot be read and the card is
|
||||
unusable.
|
||||
|
||||
3) The audio from the analog tuner is mono only. Probably caused by
|
||||
incorrect audio register information in the datasheet. We are
|
||||
waiting for updated information from Conexant.
|
||||
|
||||
4) VBI (raw or sliced) has not yet been implemented.
|
||||
|
||||
5) MPEG indexing is not yet implemented.
|
||||
|
||||
6) The driver is still a bit rough around the edges, this should
|
||||
improve over time.
|
||||
|
||||
|
||||
Firmware:
|
||||
|
||||
The firmware needs to be extracted from the Windows Hauppauge HVR-1600
|
||||
driver, available here:
|
||||
|
||||
http://hauppauge.lightpath.net/software/install_cd/hauppauge_cd_3.4d1.zip
|
||||
|
||||
Unzip, then copy the following files to the firmware directory
|
||||
and rename them as follows:
|
||||
|
||||
Drivers/Driver18/hcw18apu.rom -> v4l-cx23418-apu.fw
|
||||
Drivers/Driver18/hcw18enc.rom -> v4l-cx23418-cpu.fw
|
||||
Drivers/Driver18/hcw18mlC.rom -> v4l-cx23418-dig.fw
|
|
@ -5,16 +5,20 @@
|
|||
menu "Multimedia devices"
|
||||
depends on HAS_IOMEM
|
||||
|
||||
comment "Multimedia core support"
|
||||
|
||||
#
|
||||
# V4L core and enabled API's
|
||||
#
|
||||
|
||||
config VIDEO_DEV
|
||||
tristate "Video For Linux"
|
||||
---help---
|
||||
Support for audio/video capture and overlay devices and FM radio
|
||||
cards. The exact capabilities of each device vary.
|
||||
V4L core support for video capture and overlay devices, webcams and
|
||||
AM/FM radio cards.
|
||||
|
||||
This kernel includes support for the new Video for Linux Two API,
|
||||
(V4L2) as well as the original system. Drivers and applications
|
||||
need to be rewritten to use V4L2, but drivers for popular cards
|
||||
and applications for most video capture functions already exist.
|
||||
(V4L2).
|
||||
|
||||
Additional info and docs are available on the web at
|
||||
<http://linuxtv.org>
|
||||
|
@ -36,8 +40,11 @@ config VIDEO_ALLOW_V4L1
|
|||
default VIDEO_DEV && VIDEO_V4L2_COMMON
|
||||
select VIDEO_V4L1_COMPAT
|
||||
---help---
|
||||
Enables a compatibility API used by most V4L2 devices to allow
|
||||
its usage with legacy applications that supports only V4L1 api.
|
||||
Enables drivers based on the legacy V4L1 API.
|
||||
|
||||
This api were developed to be used at Kernel 2.2 and 2.4, but
|
||||
lacks support for several video standards. There are several
|
||||
drivers at kernel that still depends on it.
|
||||
|
||||
If you are unsure as to whether this is required, answer Y.
|
||||
|
||||
|
@ -46,9 +53,8 @@ config VIDEO_V4L1_COMPAT
|
|||
depends on VIDEO_DEV
|
||||
default VIDEO_DEV
|
||||
---help---
|
||||
This api were developed to be used at Kernel 2.2 and 2.4, but
|
||||
lacks support for several video standards. There are several
|
||||
drivers at kernel that still depends on it.
|
||||
Enables a compatibility API used by most V4L2 devices to allow
|
||||
its usage with legacy applications that supports only V4L1 api.
|
||||
|
||||
Documentation for the original API is included in the file
|
||||
<Documentation/video4linux/API.html>.
|
||||
|
@ -58,136 +64,58 @@ config VIDEO_V4L1_COMPAT
|
|||
|
||||
If you are unsure as to whether this is required, answer Y.
|
||||
|
||||
config VIDEO_V4L2
|
||||
tristate
|
||||
depends on VIDEO_DEV && VIDEO_V4L2_COMMON
|
||||
default VIDEO_DEV && VIDEO_V4L2_COMMON
|
||||
#
|
||||
# DVB Core
|
||||
#
|
||||
|
||||
config VIDEO_V4L1
|
||||
config DVB_CORE
|
||||
tristate "DVB for Linux"
|
||||
depends on NET && INET
|
||||
select CRC32
|
||||
help
|
||||
DVB core utility functions for device handling, software fallbacks etc.
|
||||
|
||||
Enable this if you own a DVB/ATSC adapter and want to use it or if
|
||||
you compile Linux for a digital SetTopBox.
|
||||
|
||||
Say Y when you have a DVB or an ATSC card and want to use it.
|
||||
|
||||
API specs and user tools are available from <http://www.linuxtv.org/>.
|
||||
|
||||
Please report problems regarding this support to the LinuxDVB
|
||||
mailing list.
|
||||
|
||||
If unsure say N.
|
||||
|
||||
config VIDEO_MEDIA
|
||||
tristate
|
||||
depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
|
||||
default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
|
||||
default DVB_CORE || VIDEO_DEV
|
||||
depends on DVB_CORE || VIDEO_DEV
|
||||
|
||||
comment "Multimedia drivers"
|
||||
|
||||
source "drivers/media/common/Kconfig"
|
||||
|
||||
#
|
||||
# Tuner drivers for DVB and V4L
|
||||
#
|
||||
|
||||
source "drivers/media/common/tuners/Kconfig"
|
||||
|
||||
#
|
||||
# Video/Radio/Hybrid adapters
|
||||
#
|
||||
|
||||
source "drivers/media/video/Kconfig"
|
||||
|
||||
source "drivers/media/radio/Kconfig"
|
||||
|
||||
#
|
||||
# DVB adapters
|
||||
#
|
||||
|
||||
source "drivers/media/dvb/Kconfig"
|
||||
|
||||
source "drivers/media/common/Kconfig"
|
||||
|
||||
config VIDEO_TUNER
|
||||
tristate
|
||||
depends on I2C
|
||||
select TUNER_XC2028 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_MT20XX if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TDA8290 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TEA5761 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TEA5767 if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_SIMPLE if !VIDEO_TUNER_CUSTOMIZE
|
||||
select TUNER_TDA9887 if !VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
menuconfig VIDEO_TUNER_CUSTOMIZE
|
||||
bool "Customize analog tuner modules to build"
|
||||
depends on VIDEO_TUNER
|
||||
help
|
||||
This allows the user to deselect tuner drivers unnecessary
|
||||
for their hardware from the build. Use this option with care
|
||||
as deselecting tuner drivers which are in fact necessary will
|
||||
result in V4L devices which cannot be tuned due to lack of
|
||||
driver support
|
||||
|
||||
If unsure say N.
|
||||
|
||||
if VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
config TUNER_XC2028
|
||||
tristate "XCeive xc2028/xc3028 tuners"
|
||||
depends on I2C && FW_LOADER
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the xc2028/xc3028 tuners.
|
||||
|
||||
config TUNER_MT20XX
|
||||
tristate "Microtune 2032 / 2050 tuners"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the MT2032 / MT2050 tuner.
|
||||
|
||||
config TUNER_TDA8290
|
||||
tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
|
||||
depends on I2C
|
||||
select DVB_TDA827X
|
||||
select DVB_TDA18271
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA8290+8275(a) tuner.
|
||||
|
||||
config TUNER_TEA5761
|
||||
tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5761 radio tuner.
|
||||
|
||||
config TUNER_TEA5767
|
||||
tristate "TEA 5767 radio tuner"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5767 radio tuner.
|
||||
|
||||
config TUNER_SIMPLE
|
||||
tristate "Simple tuner support"
|
||||
depends on I2C
|
||||
select TUNER_TDA9887
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for various simple tuners.
|
||||
|
||||
config TUNER_TDA9887
|
||||
tristate "TDA 9885/6/7 analog IF demodulator"
|
||||
depends on I2C
|
||||
default m if VIDEO_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA9885/6/7
|
||||
analog IF demodulator.
|
||||
|
||||
endif # VIDEO_TUNER_CUSTOMIZE
|
||||
|
||||
config VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_DMA_SG
|
||||
depends on HAS_DMA
|
||||
select VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_VMALLOC
|
||||
select VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_DVB
|
||||
tristate
|
||||
select VIDEOBUF_GEN
|
||||
select VIDEOBUF_DMA_SG
|
||||
|
||||
config VIDEO_BTCX
|
||||
tristate
|
||||
|
||||
config VIDEO_IR_I2C
|
||||
tristate
|
||||
|
||||
config VIDEO_IR
|
||||
tristate
|
||||
depends on INPUT
|
||||
select VIDEO_IR_I2C if I2C
|
||||
|
||||
config VIDEO_TVEEPROM
|
||||
tristate
|
||||
depends on I2C
|
||||
|
||||
config DAB
|
||||
boolean "DAB adapters"
|
||||
---help---
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
# Makefile for the kernel multimedia device drivers.
|
||||
#
|
||||
|
||||
obj-y := common/
|
||||
obj-y += video/
|
||||
obj-$(CONFIG_VIDEO_MEDIA) += common/
|
||||
|
||||
# Since hybrid devices are here, should be compiled if DVB and/or V4L
|
||||
obj-$(CONFIG_VIDEO_MEDIA) += video/
|
||||
|
||||
obj-$(CONFIG_VIDEO_DEV) += radio/
|
||||
obj-$(CONFIG_DVB_CORE) += dvb/
|
||||
ifeq ($(CONFIG_DVB_CORE),)
|
||||
obj-$(CONFIG_VIDEO_TUNER) += dvb/frontends/
|
||||
endif
|
||||
|
|
|
@ -0,0 +1,146 @@
|
|||
config MEDIA_ATTACH
|
||||
bool "Load and attach frontend driver modules as needed"
|
||||
depends on DVB_CORE
|
||||
depends on MODULES
|
||||
help
|
||||
Remove the static dependency of DVB card drivers on all
|
||||
frontend modules for all possible card variants. Instead,
|
||||
allow the card drivers to only load the frontend modules
|
||||
they require. This saves several KBytes of memory.
|
||||
|
||||
Note: You will need module-init-tools v3.2 or later for this feature.
|
||||
|
||||
If unsure say Y.
|
||||
|
||||
config MEDIA_TUNER
|
||||
tristate
|
||||
default DVB_CORE || VIDEO_DEV
|
||||
depends on DVB_CORE || VIDEO_DEV
|
||||
select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE
|
||||
select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE
|
||||
select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMIZE
|
||||
select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE
|
||||
select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMIZE
|
||||
select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMIZE
|
||||
select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
|
||||
select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMIZE
|
||||
|
||||
menuconfig MEDIA_TUNER_CUSTOMIZE
|
||||
bool "Customize analog and hybrid tuner modules to build"
|
||||
depends on MEDIA_TUNER
|
||||
help
|
||||
This allows the user to deselect tuner drivers unnecessary
|
||||
for their hardware from the build. Use this option with care
|
||||
as deselecting tuner drivers which are in fact necessary will
|
||||
result in V4L/DVB devices which cannot be tuned due to lack of
|
||||
driver support
|
||||
|
||||
If unsure say N.
|
||||
|
||||
if MEDIA_TUNER_CUSTOMIZE
|
||||
|
||||
config MEDIA_TUNER_SIMPLE
|
||||
tristate "Simple tuner support"
|
||||
depends on I2C
|
||||
select MEDIA_TUNER_TDA9887
|
||||
default m if MEDIA_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for various simple tuners.
|
||||
|
||||
config MEDIA_TUNER_TDA8290
|
||||
tristate "TDA 8290/8295 + 8275(a)/18271 tuner combo"
|
||||
depends on I2C
|
||||
select MEDIA_TUNER_TDA827X
|
||||
select MEDIA_TUNER_TDA18271
|
||||
default m if MEDIA_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA8290+8275(a) tuner.
|
||||
|
||||
config MEDIA_TUNER_TDA827X
|
||||
tristate "Philips TDA827X silicon tuner"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-T silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config MEDIA_TUNER_TDA18271
|
||||
tristate "NXP TDA18271 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config MEDIA_TUNER_TDA9887
|
||||
tristate "TDA 9885/6/7 analog IF demodulator"
|
||||
depends on I2C
|
||||
default m if MEDIA_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for Philips TDA9885/6/7
|
||||
analog IF demodulator.
|
||||
|
||||
config MEDIA_TUNER_TEA5761
|
||||
tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
|
||||
depends on I2C && EXPERIMENTAL
|
||||
default m if MEDIA_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5761 radio tuner.
|
||||
|
||||
config MEDIA_TUNER_TEA5767
|
||||
tristate "TEA 5767 radio tuner"
|
||||
depends on I2C
|
||||
default m if MEDIA_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the Philips TEA5767 radio tuner.
|
||||
|
||||
config MEDIA_TUNER_MT20XX
|
||||
tristate "Microtune 2032 / 2050 tuners"
|
||||
depends on I2C
|
||||
default m if MEDIA_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the MT2032 / MT2050 tuner.
|
||||
|
||||
config MEDIA_TUNER_MT2060
|
||||
tristate "Microtune MT2060 silicon IF tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon IF tuner MT2060 from Microtune.
|
||||
|
||||
config MEDIA_TUNER_MT2266
|
||||
tristate "Microtune MT2266 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon baseband tuner MT2266 from Microtune.
|
||||
|
||||
config MEDIA_TUNER_MT2131
|
||||
tristate "Microtune MT2131 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon baseband tuner MT2131 from Microtune.
|
||||
|
||||
config MEDIA_TUNER_QT1010
|
||||
tristate "Quantek QT1010 silicon tuner"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon tuner QT1010 from Quantek.
|
||||
|
||||
config MEDIA_TUNER_XC2028
|
||||
tristate "XCeive xc2028/xc3028 tuners"
|
||||
depends on I2C && FW_LOADER
|
||||
default m if MEDIA_TUNER_CUSTOMIZE
|
||||
help
|
||||
Say Y here to include support for the xc2028/xc3028 tuners.
|
||||
|
||||
config MEDIA_TUNER_XC5000
|
||||
tristate "Xceive XC5000 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon tuner XC5000 from Xceive.
|
||||
This device is only used inside a SiP called togther with a
|
||||
demodulator for now.
|
||||
|
||||
endif # MEDIA_TUNER_CUSTOMIZE
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Makefile for common V4L/DVB tuners
|
||||
#
|
||||
|
||||
tda18271-objs := tda18271-maps.o tda18271-common.o tda18271-fe.o
|
||||
|
||||
obj-$(CONFIG_MEDIA_TUNER_XC2028) += tuner-xc2028.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-simple.o
|
||||
# tuner-types will be merged into tuner-simple, in the future
|
||||
obj-$(CONFIG_MEDIA_TUNER_SIMPLE) += tuner-types.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_MT20XX) += mt20xx.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_TDA8290) += tda8290.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_TEA5767) += tea5767.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_TEA5761) += tea5761.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_TDA9887) += tda9887.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_TDA827X) += tda827x.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_TDA18271) += tda18271.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_XC5000) += xc5000.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_MT2060) += mt2060.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_MT2266) += mt2266.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
|
||||
obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
|
@ -30,7 +30,7 @@ struct mt2060_config {
|
|||
u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DVB_TUNER_MT2060) || (defined(CONFIG_DVB_TUNER_MT2060_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_MT2060) || (defined(CONFIG_MEDIA_TUNER_MT2060_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1);
|
||||
#else
|
||||
static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
|
||||
|
@ -38,6 +38,6 @@ static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struc
|
|||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_DVB_TUNER_MT2060
|
||||
#endif // CONFIG_MEDIA_TUNER_MT2060
|
||||
|
||||
#endif
|
|
@ -20,7 +20,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
#if defined(CONFIG_TUNER_MT20XX) || (defined(CONFIG_TUNER_MT20XX_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_MT20XX) || (defined(CONFIG_MEDIA_TUNER_MT20XX_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter* i2c_adap,
|
||||
u8 i2c_addr);
|
|
@ -30,7 +30,7 @@ struct mt2131_config {
|
|||
u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DVB_TUNER_MT2131) || (defined(CONFIG_DVB_TUNER_MT2131_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_MT2131) || (defined(CONFIG_MEDIA_TUNER_MT2131_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c,
|
||||
struct mt2131_config *cfg,
|
||||
|
@ -44,7 +44,7 @@ static inline struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
|
|||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif /* CONFIG_DVB_TUNER_MT2131 */
|
||||
#endif /* CONFIG_MEDIA_TUNER_MT2131 */
|
||||
|
||||
#endif /* __MT2131_H__ */
|
||||
|
|
@ -24,7 +24,7 @@ struct mt2266_config {
|
|||
u8 i2c_address;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DVB_TUNER_MT2266) || (defined(CONFIG_DVB_TUNER_MT2266_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_MT2266) || (defined(CONFIG_MEDIA_TUNER_MT2266_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg);
|
||||
#else
|
||||
static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg)
|
||||
|
@ -32,6 +32,6 @@ static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struc
|
|||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_DVB_TUNER_MT2266
|
||||
#endif // CONFIG_MEDIA_TUNER_MT2266
|
||||
|
||||
#endif
|
|
@ -36,7 +36,7 @@ struct qt1010_config {
|
|||
* @param cfg tuner hw based configuration
|
||||
* @return fe pointer on success, NULL on failure
|
||||
*/
|
||||
#if defined(CONFIG_DVB_TUNER_QT1010) || (defined(CONFIG_DVB_TUNER_QT1010_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_QT1010) || (defined(CONFIG_MEDIA_TUNER_QT1010_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c,
|
||||
struct qt1010_config *cfg);
|
||||
|
@ -48,6 +48,6 @@ static inline struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
|
|||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_DVB_TUNER_QT1010
|
||||
#endif // CONFIG_MEDIA_TUNER_QT1010
|
||||
|
||||
#endif
|
|
@ -81,7 +81,7 @@ struct tda18271_config {
|
|||
unsigned int small_i2c:1;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_DVB_TDA18271) || (defined(CONFIG_DVB_TDA18271_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
|
||||
struct i2c_adapter *i2c,
|
||||
struct tda18271_config *cfg);
|
|
@ -51,7 +51,7 @@ struct tda827x_config
|
|||
* @param cfg optional callback function pointers.
|
||||
* @return FE pointer on success, NULL on failure.
|
||||
*/
|
||||
#if defined(CONFIG_DVB_TDA827X) || (defined(CONFIG_DVB_TDA827X_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_TDA827X) || (defined(CONFIG_MEDIA_TUNER_TDA827X_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
|
||||
struct i2c_adapter *i2c,
|
||||
struct tda827x_config *cfg);
|
||||
|
@ -64,6 +64,6 @@ static inline struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe,
|
|||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_DVB_TDA827X
|
||||
#endif // CONFIG_MEDIA_TUNER_TDA827X
|
||||
|
||||
#endif // __DVB_TDA827X_H__
|
|
@ -578,16 +578,16 @@ static int tda829x_find_tuner(struct dvb_frontend *fe)
|
|||
|
||||
if ((data == 0x83) || (data == 0x84)) {
|
||||
priv->ver |= TDA18271;
|
||||
tda18271_attach(fe, priv->tda827x_addr,
|
||||
priv->i2c_props.adap,
|
||||
&tda829x_tda18271_config);
|
||||
dvb_attach(tda18271_attach, fe, priv->tda827x_addr,
|
||||
priv->i2c_props.adap, &tda829x_tda18271_config);
|
||||
} else {
|
||||
if ((data & 0x3c) == 0)
|
||||
priv->ver |= TDA8275;
|
||||
else
|
||||
priv->ver |= TDA8275A;
|
||||
|
||||
tda827x_attach(fe, priv->tda827x_addr, priv->i2c_props.adap, &priv->cfg);
|
||||
dvb_attach(tda827x_attach, fe, priv->tda827x_addr,
|
||||
priv->i2c_props.adap, &priv->cfg);
|
||||
priv->cfg.switch_addr = priv->i2c_props.addr;
|
||||
}
|
||||
if (fe->ops.tuner_ops.init)
|
|
@ -29,7 +29,7 @@ struct tda829x_config {
|
|||
#define TDA829X_DONT_PROBE 1
|
||||
};
|
||||
|
||||
#if defined(CONFIG_TUNER_TDA8290) || (defined(CONFIG_TUNER_TDA8290_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_TDA8290) || (defined(CONFIG_MEDIA_TUNER_TDA8290_MODULE) && defined(MODULE))
|
||||
extern int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
|
||||
|
||||
extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,
|
|
@ -21,7 +21,7 @@
|
|||
#include "dvb_frontend.h"
|
||||
|
||||
/* ------------------------------------------------------------------------ */
|
||||
#if defined(CONFIG_TUNER_TDA9887) || (defined(CONFIG_TUNER_TDA9887_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_TDA9887) || (defined(CONFIG_MEDIA_TUNER_TDA9887_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap,
|
||||
u8 i2c_addr);
|
|
@ -20,7 +20,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
#if defined(CONFIG_TUNER_TEA5761) || (defined(CONFIG_TUNER_TEA5761_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_TEA5761) || (defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE) && defined(MODULE))
|
||||
extern int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
|
||||
|
||||
extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,
|
|
@ -39,7 +39,7 @@ struct tea5767_ctrl {
|
|||
enum tea5767_xtal xtal_freq;
|
||||
};
|
||||
|
||||
#if defined(CONFIG_TUNER_TEA5767) || (defined(CONFIG_TUNER_TEA5767_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_TEA5767) || (defined(CONFIG_MEDIA_TUNER_TEA5767_MODULE) && defined(MODULE))
|
||||
extern int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
|
||||
|
||||
extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,
|
|
@ -20,7 +20,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include "dvb_frontend.h"
|
||||
|
||||
#if defined(CONFIG_TUNER_SIMPLE) || (defined(CONFIG_TUNER_SIMPLE_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_SIMPLE) || (defined(CONFIG_MEDIA_TUNER_SIMPLE_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c_adap,
|
||||
u8 i2c_addr,
|
|
@ -47,7 +47,7 @@ struct xc2028_config {
|
|||
#define XC2028_TUNER_RESET 0
|
||||
#define XC2028_RESET_CLK 1
|
||||
|
||||
#if defined(CONFIG_TUNER_XC2028) || (defined(CONFIG_TUNER_XC2028_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_XC2028) || (defined(CONFIG_MEDIA_TUNER_XC2028_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
|
||||
struct xc2028_config *cfg);
|
||||
#else
|
|
@ -45,8 +45,8 @@ struct xc5000_config {
|
|||
/* xc5000 callback command */
|
||||
#define XC5000_TUNER_RESET 0
|
||||
|
||||
#if defined(CONFIG_DVB_TUNER_XC5000) || \
|
||||
(defined(CONFIG_DVB_TUNER_XC5000_MODULE) && defined(MODULE))
|
||||
#if defined(CONFIG_MEDIA_TUNER_XC5000) || \
|
||||
(defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE))
|
||||
extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
|
||||
struct i2c_adapter *i2c,
|
||||
struct xc5000_config *cfg);
|
||||
|
@ -58,6 +58,6 @@ static inline struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
|
|||
printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
|
||||
return NULL;
|
||||
}
|
||||
#endif // CONFIG_DVB_TUNER_XC5000
|
||||
#endif // CONFIG_MEDIA_TUNER_XC5000
|
||||
|
||||
#endif // __XC5000_H__
|
|
@ -1,9 +1,7 @@
|
|||
#
|
||||
# Multimedia device configuration
|
||||
# DVB device configuration
|
||||
#
|
||||
|
||||
source "drivers/media/dvb/dvb-core/Kconfig"
|
||||
|
||||
menuconfig DVB_CAPTURE_DRIVERS
|
||||
bool "DVB/ATSC adapters"
|
||||
depends on DVB_CORE
|
||||
|
|
|
@ -9,7 +9,7 @@ config DVB_B2C2_FLEXCOP
|
|||
select DVB_STV0297 if !DVB_FE_CUSTOMISE
|
||||
select DVB_BCM3510 if !DVB_FE_CUSTOMISE
|
||||
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
|
||||
select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
|
||||
select DVB_S5H1420 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE
|
||||
select DVB_ISL6421 if !DVB_FE_CUSTOMISE
|
||||
|
|
|
@ -14,4 +14,4 @@ b2c2-flexcop-usb-objs = flexcop-usb.o
|
|||
obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-flexcop-usb.o
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
|
||||
EXTRA_CFLAGS += -Idrivers/media/video/
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners/
|
||||
|
|
|
@ -8,7 +8,7 @@ config DVB_BT8XX
|
|||
select DVB_OR51211 if !DVB_FE_CUSTOMISE
|
||||
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
|
||||
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
|
||||
select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
|
||||
select FW_LOADER
|
||||
help
|
||||
Support for PCI cards based on the Bt8xx PCI bridge. Examples are
|
||||
|
|
|
@ -3,4 +3,4 @@ obj-$(CONFIG_DVB_BT8XX) += bt878.o dvb-bt8xx.o dst.o dst_ca.o
|
|||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
||||
EXTRA_CFLAGS += -Idrivers/media/video/bt8xx
|
||||
EXTRA_CFLAGS += -Idrivers/media/video
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
||||
|
|
|
@ -1714,7 +1714,7 @@ static void dst_release(struct dvb_frontend *fe)
|
|||
struct dst_state *state = fe->demodulator_priv;
|
||||
if (state->dst_ca) {
|
||||
dvb_unregister_device(state->dst_ca);
|
||||
#ifdef CONFIG_DVB_CORE_ATTACH
|
||||
#ifdef CONFIG_MEDIA_ATTACH
|
||||
symbol_put(dst_ca_attach);
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -1,34 +0,0 @@
|
|||
config DVB_CORE
|
||||
tristate "DVB for Linux"
|
||||
depends on NET && INET
|
||||
select CRC32
|
||||
help
|
||||
Support Digital Video Broadcasting hardware. Enable this if you
|
||||
own a DVB adapter and want to use it or if you compile Linux for
|
||||
a digital SetTopBox.
|
||||
|
||||
DVB core utility functions for device handling, software fallbacks etc.
|
||||
Say Y when you have a DVB card and want to use it. Say Y if your want
|
||||
to build your drivers outside the kernel, but need the DVB core. All
|
||||
in-kernel drivers will select this automatically if needed.
|
||||
|
||||
API specs and user tools are available from <http://www.linuxtv.org/>.
|
||||
|
||||
Please report problems regarding this driver to the LinuxDVB
|
||||
mailing list.
|
||||
|
||||
If unsure say N.
|
||||
|
||||
config DVB_CORE_ATTACH
|
||||
bool "Load and attach frontend modules as needed"
|
||||
depends on DVB_CORE
|
||||
depends on MODULES
|
||||
help
|
||||
Remove the static dependency of DVB card drivers on all
|
||||
frontend modules for all possible card variants. Instead,
|
||||
allow the card drivers to only load the frontend modules
|
||||
they require. This saves several KBytes of memory.
|
||||
|
||||
Note: You will need module-init-tools v3.2 or later for this feature.
|
||||
|
||||
If unsure say Y.
|
|
@ -1189,7 +1189,7 @@ int dvb_unregister_frontend(struct dvb_frontend* fe)
|
|||
}
|
||||
EXPORT_SYMBOL(dvb_unregister_frontend);
|
||||
|
||||
#ifdef CONFIG_DVB_CORE_ATTACH
|
||||
#ifdef CONFIG_MEDIA_ATTACH
|
||||
void dvb_frontend_detach(struct dvb_frontend* fe)
|
||||
{
|
||||
void *ptr;
|
||||
|
|
|
@ -115,7 +115,7 @@ extern int dvb_usercopy(struct inode *inode, struct file *file,
|
|||
unsigned int cmd, void *arg));
|
||||
|
||||
/** generic DVB attach function. */
|
||||
#ifdef CONFIG_DVB_CORE_ATTACH
|
||||
#ifdef CONFIG_MEDIA_ATTACH
|
||||
#define dvb_attach(FUNCTION, ARGS...) ({ \
|
||||
void *__r = NULL; \
|
||||
typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
|
||||
|
|
|
@ -25,7 +25,7 @@ config DVB_USB_A800
|
|||
tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
|
||||
depends on DVB_USB
|
||||
select DVB_DIB3000MC
|
||||
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select DVB_PLL if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
|
||||
|
@ -35,7 +35,7 @@ config DVB_USB_DIBUSB_MB
|
|||
depends on DVB_USB
|
||||
select DVB_PLL if !DVB_FE_CUSTOMISE
|
||||
select DVB_DIB3000MB
|
||||
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
|
||||
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
|
||||
|
@ -56,7 +56,7 @@ config DVB_USB_DIBUSB_MC
|
|||
tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
|
||||
depends on DVB_USB
|
||||
select DVB_DIB3000MC
|
||||
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Support for USB2.0 DVB-T receivers based on reference designs made by
|
||||
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
|
||||
|
@ -73,8 +73,8 @@ config DVB_USB_DIB0700
|
|||
select DVB_DIB7000P
|
||||
select DVB_DIB7000M
|
||||
select DVB_DIB3000MC
|
||||
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TUNER_MT2266 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TUNER_DIB0070
|
||||
help
|
||||
Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
|
||||
|
@ -93,7 +93,7 @@ config DVB_USB_UMT_010
|
|||
depends on DVB_USB
|
||||
select DVB_PLL if !DVB_FE_CUSTOMISE
|
||||
select DVB_DIB3000MC
|
||||
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
|
||||
|
||||
|
@ -105,7 +105,7 @@ config DVB_USB_CXUSB
|
|||
select DVB_LGDT330X if !DVB_FE_CUSTOMISE
|
||||
select DVB_MT352 if !DVB_FE_CUSTOMISE
|
||||
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
|
||||
select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_SIMPLE if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the Conexant USB2.0 hybrid reference design.
|
||||
Currently, only DVB and ATSC modes are supported, analog mode
|
||||
|
@ -118,7 +118,7 @@ config DVB_USB_M920X
|
|||
tristate "Uli m920x DVB-T USB2.0 support"
|
||||
depends on DVB_USB
|
||||
select DVB_MT352 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
|
||||
Currently, only devices with a product id of
|
||||
|
@ -129,7 +129,7 @@ config DVB_USB_GL861
|
|||
tristate "Genesys Logic GL861 USB2.0 support"
|
||||
depends on DVB_USB
|
||||
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
|
||||
receiver with USB ID 0db0:5581.
|
||||
|
@ -138,7 +138,7 @@ config DVB_USB_AU6610
|
|||
tristate "Alcor Micro AU6610 USB2.0 support"
|
||||
depends on DVB_USB
|
||||
select DVB_ZL10353 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
|
||||
|
||||
|
@ -190,7 +190,7 @@ config DVB_USB_NOVA_T_USB2
|
|||
tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
|
||||
depends on DVB_USB
|
||||
select DVB_DIB3000MC
|
||||
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select DVB_PLL if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
|
||||
|
@ -227,8 +227,8 @@ config DVB_USB_OPERA1
|
|||
config DVB_USB_AF9005
|
||||
tristate "Afatech AF9005 DVB-T USB1.1 support"
|
||||
depends on DVB_USB && EXPERIMENTAL
|
||||
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
|
||||
select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
|
||||
help
|
||||
Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
|
||||
and the TerraTec Cinergy T USB XE (Rev.1)
|
||||
|
|
|
@ -63,5 +63,5 @@ obj-$(CONFIG_DVB_USB_AF9005_REMOTE) += dvb-usb-af9005-remote.o
|
|||
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
|
||||
# due to tuner-xc3028
|
||||
EXTRA_CFLAGS += -Idrivers/media/video
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
||||
|
||||
|
|
|
@ -15,13 +15,6 @@ config DVB_FE_CUSTOMISE
|
|||
comment "DVB-S (satellite) frontends"
|
||||
depends on DVB_CORE
|
||||
|
||||
config DVB_STV0299
|
||||
tristate "ST STV0299 based"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_CX24110
|
||||
tristate "Conexant CX24110 based"
|
||||
depends on DVB_CORE && I2C
|
||||
|
@ -36,13 +29,6 @@ config DVB_CX24123
|
|||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_TDA8083
|
||||
tristate "Philips TDA8083 based"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_MT312
|
||||
tristate "Zarlink VP310/MT312 based"
|
||||
depends on DVB_CORE && I2C
|
||||
|
@ -50,13 +36,6 @@ config DVB_MT312
|
|||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_VES1X93
|
||||
tristate "VLSI VES1893 or VES1993 based"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_S5H1420
|
||||
tristate "Samsung S5H1420 based"
|
||||
depends on DVB_CORE && I2C
|
||||
|
@ -64,6 +43,20 @@ config DVB_S5H1420
|
|||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_STV0299
|
||||
tristate "ST STV0299 based"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_TDA8083
|
||||
tristate "Philips TDA8083 based"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_TDA10086
|
||||
tristate "Philips TDA10086 based"
|
||||
depends on DVB_CORE && I2C
|
||||
|
@ -71,6 +64,34 @@ config DVB_TDA10086
|
|||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_VES1X93
|
||||
tristate "VLSI VES1893 or VES1993 based"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_TUNER_ITD1000
|
||||
tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S tuner module. Say Y when you want to support this frontend.
|
||||
|
||||
config DVB_TDA826X
|
||||
tristate "Philips TDA826X silicon tuner"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config DVB_TUA6100
|
||||
tristate "Infineon TUA6100 PLL"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S PLL chip.
|
||||
|
||||
comment "DVB-T (terrestrial) frontends"
|
||||
depends on DVB_CORE
|
||||
|
||||
|
@ -315,7 +336,7 @@ config DVB_S5H1411
|
|||
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
|
||||
to support this frontend.
|
||||
|
||||
comment "Tuners/PLL support"
|
||||
comment "Digital terrestrial only tuners/PLL"
|
||||
depends on DVB_CORE
|
||||
|
||||
config DVB_PLL
|
||||
|
@ -326,55 +347,6 @@ config DVB_PLL
|
|||
This module drives a number of tuners based on PLL chips with a
|
||||
common I2C interface. Say Y when you want to support these tuners.
|
||||
|
||||
config DVB_TDA826X
|
||||
tristate "Philips TDA826X silicon tuner"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-S silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config DVB_TDA827X
|
||||
tristate "Philips TDA827X silicon tuner"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVB-T silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config DVB_TDA18271
|
||||
tristate "NXP TDA18271 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A silicon tuner module. Say Y when you want to support this tuner.
|
||||
|
||||
config DVB_TUNER_QT1010
|
||||
tristate "Quantek QT1010 silicon tuner"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon tuner QT1010 from Quantek.
|
||||
|
||||
config DVB_TUNER_MT2060
|
||||
tristate "Microtune MT2060 silicon IF tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon IF tuner MT2060 from Microtune.
|
||||
|
||||
config DVB_TUNER_MT2266
|
||||
tristate "Microtune MT2266 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon baseband tuner MT2266 from Microtune.
|
||||
|
||||
config DVB_TUNER_MT2131
|
||||
tristate "Microtune MT2131 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon baseband tuner MT2131 from Microtune.
|
||||
|
||||
config DVB_TUNER_DIB0070
|
||||
tristate "DiBcom DiB0070 silicon base-band tuner"
|
||||
depends on I2C
|
||||
|
@ -384,21 +356,7 @@ config DVB_TUNER_DIB0070
|
|||
This device is only used inside a SiP called togther with a
|
||||
demodulator for now.
|
||||
|
||||
config DVB_TUNER_XC5000
|
||||
tristate "Xceive XC5000 silicon tuner"
|
||||
depends on I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A driver for the silicon tuner XC5000 from Xceive.
|
||||
This device is only used inside a SiP called togther with a
|
||||
demodulator for now.
|
||||
|
||||
config DVB_TUNER_ITD1000
|
||||
tristate "Integrant ITD1000 Zero IF tuner for DVB-S/DSS"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
|
||||
comment "Miscellaneous devices"
|
||||
comment "SEC control devices for DVB-S"
|
||||
depends on DVB_CORE
|
||||
|
||||
config DVB_LNBP21
|
||||
|
@ -422,11 +380,4 @@ config DVB_ISL6421
|
|||
help
|
||||
An SEC control chip.
|
||||
|
||||
config DVB_TUA6100
|
||||
tristate "TUA6100 PLL"
|
||||
depends on DVB_CORE && I2C
|
||||
default m if DVB_FE_CUSTOMISE
|
||||
help
|
||||
A DVBS PLL chip.
|
||||
|
||||
endmenu
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
#
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
|
||||
EXTRA_CFLAGS += -Idrivers/media/video/
|
||||
|
||||
tda18271-objs := tda18271-tables.o tda18271-common.o tda18271-fe.o
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners/
|
||||
|
||||
obj-$(CONFIG_DVB_PLL) += dvb-pll.o
|
||||
obj-$(CONFIG_DVB_STV0299) += stv0299.o
|
||||
|
@ -42,16 +40,9 @@ obj-$(CONFIG_DVB_ISL6405) += isl6405.o
|
|||
obj-$(CONFIG_DVB_ISL6421) += isl6421.o
|
||||
obj-$(CONFIG_DVB_TDA10086) += tda10086.o
|
||||
obj-$(CONFIG_DVB_TDA826X) += tda826x.o
|
||||
obj-$(CONFIG_DVB_TDA827X) += tda827x.o
|
||||
obj-$(CONFIG_DVB_TDA18271) += tda18271.o
|
||||
obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
|
||||
obj-$(CONFIG_DVB_TUNER_MT2266) += mt2266.o
|
||||
obj-$(CONFIG_DVB_TUNER_DIB0070) += dib0070.o
|
||||
obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
|
||||
obj-$(CONFIG_DVB_TUA6100) += tua6100.o
|
||||
obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
|
||||
obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
|
||||
obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
|
||||
obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
|
||||
obj-$(CONFIG_DVB_AU8522) += au8522.o
|
||||
obj-$(CONFIG_DVB_TDA10048) += tda10048.o
|
||||
|
|
|
@ -481,7 +481,7 @@ static void s5h1420_setsymbolrate(struct s5h1420_state* state,
|
|||
val *= 2;
|
||||
do_div(val, (state->fclk / 1000));
|
||||
|
||||
dprintk("symbol rate register: %06llx\n", val);
|
||||
dprintk("symbol rate register: %06llx\n", (unsigned long long)val);
|
||||
|
||||
v = s5h1420_readreg(state, Loop01);
|
||||
s5h1420_writereg(state, Loop01, v & 0x7f);
|
||||
|
|
|
@ -1,3 +1,49 @@
|
|||
#
|
||||
# Generic video config states
|
||||
#
|
||||
|
||||
config VIDEO_V4L2
|
||||
tristate
|
||||
depends on VIDEO_DEV && VIDEO_V4L2_COMMON
|
||||
default VIDEO_DEV && VIDEO_V4L2_COMMON
|
||||
|
||||
config VIDEO_V4L1
|
||||
tristate
|
||||
depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
|
||||
default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1
|
||||
|
||||
config VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_DMA_SG
|
||||
depends on HAS_DMA
|
||||
select VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_VMALLOC
|
||||
select VIDEOBUF_GEN
|
||||
tristate
|
||||
|
||||
config VIDEOBUF_DVB
|
||||
tristate
|
||||
select VIDEOBUF_GEN
|
||||
select VIDEOBUF_DMA_SG
|
||||
|
||||
config VIDEO_BTCX
|
||||
tristate
|
||||
|
||||
config VIDEO_IR_I2C
|
||||
tristate
|
||||
|
||||
config VIDEO_IR
|
||||
tristate
|
||||
depends on INPUT
|
||||
select VIDEO_IR_I2C if I2C
|
||||
|
||||
config VIDEO_TVEEPROM
|
||||
tristate
|
||||
depends on I2C
|
||||
|
||||
#
|
||||
# Multimedia Video device configuration
|
||||
#
|
||||
|
@ -644,7 +690,7 @@ config VIDEO_MXB
|
|||
tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
|
||||
depends on PCI && VIDEO_V4L1 && I2C
|
||||
select VIDEO_SAA7146_VV
|
||||
select VIDEO_TUNER
|
||||
select MEDIA_TUNER
|
||||
select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
|
||||
select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
|
||||
select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
|
||||
|
@ -702,6 +748,8 @@ source "drivers/media/video/au0828/Kconfig"
|
|||
|
||||
source "drivers/media/video/ivtv/Kconfig"
|
||||
|
||||
source "drivers/media/video/cx18/Kconfig"
|
||||
|
||||
config VIDEO_M32R_AR
|
||||
tristate "AR devices"
|
||||
depends on M32R && VIDEO_V4L1
|
||||
|
|
|
@ -84,17 +84,7 @@ obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
|
|||
obj-$(CONFIG_VIDEO_DPC) += dpc7146.o
|
||||
obj-$(CONFIG_TUNER_3036) += tuner-3036.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
|
||||
|
||||
obj-$(CONFIG_TUNER_XC2028) += tuner-xc2028.o
|
||||
obj-$(CONFIG_TUNER_SIMPLE) += tuner-simple.o
|
||||
# tuner-types will be merged into tuner-simple, in the future
|
||||
obj-$(CONFIG_TUNER_SIMPLE) += tuner-types.o
|
||||
obj-$(CONFIG_TUNER_MT20XX) += mt20xx.o
|
||||
obj-$(CONFIG_TUNER_TDA8290) += tda8290.o
|
||||
obj-$(CONFIG_TUNER_TEA5767) += tea5767.o
|
||||
obj-$(CONFIG_TUNER_TEA5761) += tea5761.o
|
||||
obj-$(CONFIG_TUNER_TDA9887) += tda9887.o
|
||||
obj-$(CONFIG_MEDIA_TUNER) += tuner.o
|
||||
|
||||
obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
|
||||
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o
|
||||
|
@ -134,6 +124,7 @@ obj-$(CONFIG_USB_VICAM) += usbvideo/
|
|||
obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/
|
||||
|
||||
obj-$(CONFIG_VIDEO_IVTV) += ivtv/
|
||||
obj-$(CONFIG_VIDEO_CX18) += cx18/
|
||||
|
||||
obj-$(CONFIG_VIDEO_VIVI) += vivi.o
|
||||
obj-$(CONFIG_VIDEO_CX23885) += cx23885/
|
||||
|
@ -147,3 +138,4 @@ obj-$(CONFIG_VIDEO_AU0828) += au0828/
|
|||
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
||||
|
|
|
@ -4,7 +4,7 @@ config VIDEO_AU0828
|
|||
depends on VIDEO_DEV && I2C && INPUT && DVB_CORE
|
||||
select I2C_ALGOBIT
|
||||
select DVB_AU8522 if !DVB_FE_CUSTOMIZE
|
||||
select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
|
||||
select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
|
||||
---help---
|
||||
This is a video4linux driver for Auvitek's USB device.
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o
|
|||
|
||||
obj-$(CONFIG_VIDEO_AU0828) += au0828.o
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/media/video
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ config VIDEO_BT848
|
|||
select VIDEO_BTCX
|
||||
select VIDEOBUF_DMA_SG
|
||||
select VIDEO_IR
|
||||
select VIDEO_TUNER
|
||||
select MEDIA_TUNER
|
||||
select VIDEO_TVEEPROM
|
||||
select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
|
||||
select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO
|
||||
|
|
|
@ -9,4 +9,5 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
|
|||
obj-$(CONFIG_VIDEO_BT848) += bttv.o
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/media/video
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
config VIDEO_CX18
|
||||
tristate "Conexant cx23418 MPEG encoder support"
|
||||
depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL
|
||||
select I2C_ALGOBIT
|
||||
select FW_LOADER
|
||||
select VIDEO_IR
|
||||
select VIDEO_TUNER
|
||||
select VIDEO_TVEEPROM
|
||||
select VIDEO_CX2341X
|
||||
select VIDEO_CS5345
|
||||
select DVB_S5H1409
|
||||
---help---
|
||||
This is a video4linux driver for Conexant cx23418 based
|
||||
PCI combo video recorder devices.
|
||||
|
||||
This is used in devices such as the Hauppauge HVR-1600
|
||||
cards.
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called cx18.
|
|
@ -0,0 +1,11 @@
|
|||
cx18-objs := cx18-driver.o cx18-cards.o cx18-i2c.o cx18-firmware.o cx18-gpio.o \
|
||||
cx18-queue.o cx18-streams.o cx18-fileops.o cx18-ioctl.o cx18-controls.o \
|
||||
cx18-mailbox.o cx18-vbi.o cx18-audio.o cx18-video.o cx18-irq.o \
|
||||
cx18-av-core.o cx18-av-audio.o cx18-av-firmware.o cx18-av-vbi.o cx18-scb.o \
|
||||
cx18-dvb.o
|
||||
|
||||
obj-$(CONFIG_VIDEO_CX18) += cx18.o
|
||||
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
|
||||
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
|
||||
EXTRA_CFLAGS += -Idrivers/media/common/tuners
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* cx18 audio-related functions
|
||||
*
|
||||
* Derived from ivtv-audio.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-i2c.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-audio.h"
|
||||
|
||||
/* Selects the audio input and output according to the current
|
||||
settings. */
|
||||
int cx18_audio_set_io(struct cx18 *cx)
|
||||
{
|
||||
struct v4l2_routing route;
|
||||
u32 audio_input;
|
||||
int mux_input;
|
||||
|
||||
/* Determine which input to use */
|
||||
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
|
||||
audio_input = cx->card->radio_input.audio_input;
|
||||
mux_input = cx->card->radio_input.muxer_input;
|
||||
} else {
|
||||
audio_input =
|
||||
cx->card->audio_inputs[cx->audio_input].audio_input;
|
||||
mux_input =
|
||||
cx->card->audio_inputs[cx->audio_input].muxer_input;
|
||||
}
|
||||
|
||||
/* handle muxer chips */
|
||||
route.input = mux_input;
|
||||
route.output = 0;
|
||||
cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
|
||||
|
||||
route.input = audio_input;
|
||||
return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
|
||||
VIDIOC_INT_S_AUDIO_ROUTING, &route);
|
||||
}
|
||||
|
||||
void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
|
||||
{
|
||||
cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
|
||||
VIDIOC_INT_S_AUDIO_ROUTING, route);
|
||||
}
|
||||
|
||||
void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq)
|
||||
{
|
||||
static u32 freqs[3] = { 44100, 48000, 32000 };
|
||||
|
||||
/* The audio clock of the digitizer must match the codec sample
|
||||
rate otherwise you get some very strange effects. */
|
||||
if (freq > 2)
|
||||
return;
|
||||
cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* cx18 audio-related functions
|
||||
*
|
||||
* Derived from ivtv-audio.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
int cx18_audio_set_io(struct cx18 *cx);
|
||||
void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route);
|
||||
void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq);
|
|
@ -0,0 +1,361 @@
|
|||
/*
|
||||
* cx18 ADEC audio functions
|
||||
*
|
||||
* Derived from cx25840-audio.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
|
||||
static int set_audclk_freq(struct cx18 *cx, u32 freq)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
|
||||
if (freq != 32000 && freq != 44100 && freq != 48000)
|
||||
return -EINVAL;
|
||||
|
||||
/* common for all inputs and rates */
|
||||
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x10 */
|
||||
cx18_av_write(cx, 0x127, 0x50);
|
||||
|
||||
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
/* VID_PLL and AUX_PLL */
|
||||
cx18_av_write4(cx, 0x108, 0x1006040f);
|
||||
|
||||
/* AUX_PLL_FRAC */
|
||||
cx18_av_write4(cx, 0x110, 0x01bb39ee);
|
||||
|
||||
/* src3/4/6_ctl = 0x0801f77f */
|
||||
cx18_av_write4(cx, 0x900, 0x0801f77f);
|
||||
cx18_av_write4(cx, 0x904, 0x0801f77f);
|
||||
cx18_av_write4(cx, 0x90c, 0x0801f77f);
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
/* VID_PLL and AUX_PLL */
|
||||
cx18_av_write4(cx, 0x108, 0x1009040f);
|
||||
|
||||
/* AUX_PLL_FRAC */
|
||||
cx18_av_write4(cx, 0x110, 0x00ec6bd6);
|
||||
|
||||
/* src3/4/6_ctl = 0x08016d59 */
|
||||
cx18_av_write4(cx, 0x900, 0x08016d59);
|
||||
cx18_av_write4(cx, 0x904, 0x08016d59);
|
||||
cx18_av_write4(cx, 0x90c, 0x08016d59);
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
/* VID_PLL and AUX_PLL */
|
||||
cx18_av_write4(cx, 0x108, 0x100a040f);
|
||||
|
||||
/* AUX_PLL_FRAC */
|
||||
cx18_av_write4(cx, 0x110, 0x0098d6e5);
|
||||
|
||||
/* src3/4/6_ctl = 0x08014faa */
|
||||
cx18_av_write4(cx, 0x900, 0x08014faa);
|
||||
cx18_av_write4(cx, 0x904, 0x08014faa);
|
||||
cx18_av_write4(cx, 0x90c, 0x08014faa);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
switch (freq) {
|
||||
case 32000:
|
||||
/* VID_PLL and AUX_PLL */
|
||||
cx18_av_write4(cx, 0x108, 0x1e08040f);
|
||||
|
||||
/* AUX_PLL_FRAC */
|
||||
cx18_av_write4(cx, 0x110, 0x012a0869);
|
||||
|
||||
/* src1_ctl = 0x08010000 */
|
||||
cx18_av_write4(cx, 0x8f8, 0x08010000);
|
||||
|
||||
/* src3/4/6_ctl = 0x08020000 */
|
||||
cx18_av_write4(cx, 0x900, 0x08020000);
|
||||
cx18_av_write4(cx, 0x904, 0x08020000);
|
||||
cx18_av_write4(cx, 0x90c, 0x08020000);
|
||||
|
||||
/* SA_MCLK_SEL=1, SA_MCLK_DIV=0x14 */
|
||||
cx18_av_write(cx, 0x127, 0x54);
|
||||
break;
|
||||
|
||||
case 44100:
|
||||
/* VID_PLL and AUX_PLL */
|
||||
cx18_av_write4(cx, 0x108, 0x1809040f);
|
||||
|
||||
/* AUX_PLL_FRAC */
|
||||
cx18_av_write4(cx, 0x110, 0x00ec6bd6);
|
||||
|
||||
/* src1_ctl = 0x08010000 */
|
||||
cx18_av_write4(cx, 0x8f8, 0x080160cd);
|
||||
|
||||
/* src3/4/6_ctl = 0x08020000 */
|
||||
cx18_av_write4(cx, 0x900, 0x08017385);
|
||||
cx18_av_write4(cx, 0x904, 0x08017385);
|
||||
cx18_av_write4(cx, 0x90c, 0x08017385);
|
||||
break;
|
||||
|
||||
case 48000:
|
||||
/* VID_PLL and AUX_PLL */
|
||||
cx18_av_write4(cx, 0x108, 0x180a040f);
|
||||
|
||||
/* AUX_PLL_FRAC */
|
||||
cx18_av_write4(cx, 0x110, 0x0098d6e5);
|
||||
|
||||
/* src1_ctl = 0x08010000 */
|
||||
cx18_av_write4(cx, 0x8f8, 0x08018000);
|
||||
|
||||
/* src3/4/6_ctl = 0x08020000 */
|
||||
cx18_av_write4(cx, 0x900, 0x08015555);
|
||||
cx18_av_write4(cx, 0x904, 0x08015555);
|
||||
cx18_av_write4(cx, 0x90c, 0x08015555);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state->audclk_freq = freq;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cx18_av_audio_set_path(struct cx18 *cx)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
|
||||
/* stop microcontroller */
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0);
|
||||
|
||||
/* assert soft reset */
|
||||
cx18_av_and_or(cx, 0x810, ~0x1, 0x01);
|
||||
|
||||
/* Mute everything to prevent the PFFT! */
|
||||
cx18_av_write(cx, 0x8d3, 0x1f);
|
||||
|
||||
if (state->aud_input == CX18_AV_AUDIO_SERIAL) {
|
||||
/* Set Path1 to Serial Audio Input */
|
||||
cx18_av_write4(cx, 0x8d0, 0x01011012);
|
||||
|
||||
/* The microcontroller should not be started for the
|
||||
* non-tuner inputs: autodetection is specific for
|
||||
* TV audio. */
|
||||
} else {
|
||||
/* Set Path1 to Analog Demod Main Channel */
|
||||
cx18_av_write4(cx, 0x8d0, 0x1f063870);
|
||||
}
|
||||
|
||||
set_audclk_freq(cx, state->audclk_freq);
|
||||
|
||||
/* deassert soft reset */
|
||||
cx18_av_and_or(cx, 0x810, ~0x1, 0x00);
|
||||
|
||||
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
|
||||
/* When the microcontroller detects the
|
||||
* audio format, it will unmute the lines */
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_volume(struct cx18 *cx)
|
||||
{
|
||||
/* Volume runs +18dB to -96dB in 1/2dB steps
|
||||
* change to fit the msp3400 -114dB to +12dB range */
|
||||
|
||||
/* check PATH1_VOLUME */
|
||||
int vol = 228 - cx18_av_read(cx, 0x8d4);
|
||||
vol = (vol / 2) + 23;
|
||||
return vol << 9;
|
||||
}
|
||||
|
||||
static void set_volume(struct cx18 *cx, int volume)
|
||||
{
|
||||
/* First convert the volume to msp3400 values (0-127) */
|
||||
int vol = volume >> 9;
|
||||
/* now scale it up to cx18_av values
|
||||
* -114dB to -96dB maps to 0
|
||||
* this should be 19, but in my testing that was 4dB too loud */
|
||||
if (vol <= 23)
|
||||
vol = 0;
|
||||
else
|
||||
vol -= 23;
|
||||
|
||||
/* PATH1_VOLUME */
|
||||
cx18_av_write(cx, 0x8d4, 228 - (vol * 2));
|
||||
}
|
||||
|
||||
static int get_bass(struct cx18 *cx)
|
||||
{
|
||||
/* bass is 49 steps +12dB to -12dB */
|
||||
|
||||
/* check PATH1_EQ_BASS_VOL */
|
||||
int bass = cx18_av_read(cx, 0x8d9) & 0x3f;
|
||||
bass = (((48 - bass) * 0xffff) + 47) / 48;
|
||||
return bass;
|
||||
}
|
||||
|
||||
static void set_bass(struct cx18 *cx, int bass)
|
||||
{
|
||||
/* PATH1_EQ_BASS_VOL */
|
||||
cx18_av_and_or(cx, 0x8d9, ~0x3f, 48 - (bass * 48 / 0xffff));
|
||||
}
|
||||
|
||||
static int get_treble(struct cx18 *cx)
|
||||
{
|
||||
/* treble is 49 steps +12dB to -12dB */
|
||||
|
||||
/* check PATH1_EQ_TREBLE_VOL */
|
||||
int treble = cx18_av_read(cx, 0x8db) & 0x3f;
|
||||
treble = (((48 - treble) * 0xffff) + 47) / 48;
|
||||
return treble;
|
||||
}
|
||||
|
||||
static void set_treble(struct cx18 *cx, int treble)
|
||||
{
|
||||
/* PATH1_EQ_TREBLE_VOL */
|
||||
cx18_av_and_or(cx, 0x8db, ~0x3f, 48 - (treble * 48 / 0xffff));
|
||||
}
|
||||
|
||||
static int get_balance(struct cx18 *cx)
|
||||
{
|
||||
/* balance is 7 bit, 0 to -96dB */
|
||||
|
||||
/* check PATH1_BAL_LEVEL */
|
||||
int balance = cx18_av_read(cx, 0x8d5) & 0x7f;
|
||||
/* check PATH1_BAL_LEFT */
|
||||
if ((cx18_av_read(cx, 0x8d5) & 0x80) == 0)
|
||||
balance = 0x80 - balance;
|
||||
else
|
||||
balance = 0x80 + balance;
|
||||
return balance << 8;
|
||||
}
|
||||
|
||||
static void set_balance(struct cx18 *cx, int balance)
|
||||
{
|
||||
int bal = balance >> 8;
|
||||
if (bal > 0x80) {
|
||||
/* PATH1_BAL_LEFT */
|
||||
cx18_av_and_or(cx, 0x8d5, 0x7f, 0x80);
|
||||
/* PATH1_BAL_LEVEL */
|
||||
cx18_av_and_or(cx, 0x8d5, ~0x7f, bal & 0x7f);
|
||||
} else {
|
||||
/* PATH1_BAL_LEFT */
|
||||
cx18_av_and_or(cx, 0x8d5, 0x7f, 0x00);
|
||||
/* PATH1_BAL_LEVEL */
|
||||
cx18_av_and_or(cx, 0x8d5, ~0x7f, 0x80 - bal);
|
||||
}
|
||||
}
|
||||
|
||||
static int get_mute(struct cx18 *cx)
|
||||
{
|
||||
/* check SRC1_MUTE_EN */
|
||||
return cx18_av_read(cx, 0x8d3) & 0x2 ? 1 : 0;
|
||||
}
|
||||
|
||||
static void set_mute(struct cx18 *cx, int mute)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
|
||||
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
|
||||
/* Must turn off microcontroller in order to mute sound.
|
||||
* Not sure if this is the best method, but it does work.
|
||||
* If the microcontroller is running, then it will undo any
|
||||
* changes to the mute register. */
|
||||
if (mute) {
|
||||
/* disable microcontroller */
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
|
||||
cx18_av_write(cx, 0x8d3, 0x1f);
|
||||
} else {
|
||||
/* enable microcontroller */
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
|
||||
}
|
||||
} else {
|
||||
/* SRC1_MUTE_EN */
|
||||
cx18_av_and_or(cx, 0x8d3, ~0x2, mute ? 0x02 : 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
struct v4l2_control *ctrl = arg;
|
||||
int retval;
|
||||
|
||||
switch (cmd) {
|
||||
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
|
||||
if (state->aud_input != CX18_AV_AUDIO_SERIAL) {
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0);
|
||||
cx18_av_write(cx, 0x8d3, 0x1f);
|
||||
}
|
||||
cx18_av_and_or(cx, 0x810, ~0x1, 1);
|
||||
retval = set_audclk_freq(cx, *(u32 *)arg);
|
||||
cx18_av_and_or(cx, 0x810, ~0x1, 0);
|
||||
if (state->aud_input != CX18_AV_AUDIO_SERIAL)
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
|
||||
return retval;
|
||||
|
||||
case VIDIOC_G_CTRL:
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
ctrl->value = get_volume(cx);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
ctrl->value = get_bass(cx);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
ctrl->value = get_treble(cx);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
ctrl->value = get_balance(cx);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
ctrl->value = get_mute(cx);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
case VIDIOC_S_CTRL:
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
set_volume(cx, ctrl->value);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
set_bass(cx, ctrl->value);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
set_treble(cx, ctrl->value);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
set_balance(cx, ctrl->value);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
set_mute(cx, ctrl->value);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,879 @@
|
|||
/*
|
||||
* cx18 ADEC audio functions
|
||||
*
|
||||
* Derived from cx25840-core.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
|
||||
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
|
||||
{
|
||||
u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
|
||||
u32 mask = 0xff;
|
||||
int shift = (addr & 3) * 8;
|
||||
|
||||
x = (x & ~(mask << shift)) | ((u32)value << shift);
|
||||
writel(x, cx->reg_mem + 0xc40000 + (addr & ~3));
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value)
|
||||
{
|
||||
writel(value, cx->reg_mem + 0xc40000 + addr);
|
||||
return 0;
|
||||
}
|
||||
|
||||
u8 cx18_av_read(struct cx18 *cx, u16 addr)
|
||||
{
|
||||
u32 x = readl(cx->reg_mem + 0xc40000 + (addr & ~3));
|
||||
int shift = (addr & 3) * 8;
|
||||
|
||||
return (x >> shift) & 0xff;
|
||||
}
|
||||
|
||||
u32 cx18_av_read4(struct cx18 *cx, u16 addr)
|
||||
{
|
||||
return readl(cx->reg_mem + 0xc40000 + addr);
|
||||
}
|
||||
|
||||
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned and_mask,
|
||||
u8 or_value)
|
||||
{
|
||||
return cx18_av_write(cx, addr,
|
||||
(cx18_av_read(cx, addr) & and_mask) |
|
||||
or_value);
|
||||
}
|
||||
|
||||
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
|
||||
u32 or_value)
|
||||
{
|
||||
return cx18_av_write4(cx, addr,
|
||||
(cx18_av_read4(cx, addr) & and_mask) |
|
||||
or_value);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
|
||||
enum cx18_av_audio_input aud_input);
|
||||
static void log_audio_status(struct cx18 *cx);
|
||||
static void log_video_status(struct cx18 *cx);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static void cx18_av_initialize(struct cx18 *cx)
|
||||
{
|
||||
u32 v;
|
||||
|
||||
cx18_av_loadfw(cx);
|
||||
/* Stop 8051 code execution */
|
||||
cx18_av_write4(cx, CXADEC_DL_CTL, 0x03000000);
|
||||
|
||||
/* initallize the PLL by toggling sleep bit */
|
||||
v = cx18_av_read4(cx, CXADEC_HOST_REG1);
|
||||
/* enable sleep mode */
|
||||
cx18_av_write4(cx, CXADEC_HOST_REG1, v | 1);
|
||||
/* disable sleep mode */
|
||||
cx18_av_write4(cx, CXADEC_HOST_REG1, v & 0xfffe);
|
||||
|
||||
/* initialize DLLs */
|
||||
v = cx18_av_read4(cx, CXADEC_DLL1_DIAG_CTRL) & 0xE1FFFEFF;
|
||||
/* disable FLD */
|
||||
cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v);
|
||||
/* enable FLD */
|
||||
cx18_av_write4(cx, CXADEC_DLL1_DIAG_CTRL, v | 0x10000100);
|
||||
|
||||
v = cx18_av_read4(cx, CXADEC_DLL2_DIAG_CTRL) & 0xE1FFFEFF;
|
||||
/* disable FLD */
|
||||
cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v);
|
||||
/* enable FLD */
|
||||
cx18_av_write4(cx, CXADEC_DLL2_DIAG_CTRL, v | 0x06000100);
|
||||
|
||||
/* set analog bias currents. Set Vreg to 1.20V. */
|
||||
cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL1, 0x000A1802);
|
||||
|
||||
v = cx18_av_read4(cx, CXADEC_AFE_DIAG_CTRL3) | 1;
|
||||
/* enable TUNE_FIL_RST */
|
||||
cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v);
|
||||
/* disable TUNE_FIL_RST */
|
||||
cx18_av_write4(cx, CXADEC_AFE_DIAG_CTRL3, v & 0xFFFFFFFE);
|
||||
|
||||
/* enable 656 output */
|
||||
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x040C00);
|
||||
|
||||
/* video output drive strength */
|
||||
cx18_av_and_or4(cx, CXADEC_PIN_CTRL2, ~0, 0x2);
|
||||
|
||||
/* reset video */
|
||||
cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0x8000);
|
||||
cx18_av_write4(cx, CXADEC_SOFT_RST_CTRL, 0);
|
||||
|
||||
/* set video to auto-detect */
|
||||
/* Clear bits 11-12 to enable slow locking mode. Set autodetect mode */
|
||||
/* set the comb notch = 1 */
|
||||
cx18_av_and_or4(cx, CXADEC_MODE_CTRL, 0xFFF7E7F0, 0x02040800);
|
||||
|
||||
/* Enable wtw_en in CRUSH_CTRL (Set bit 22) */
|
||||
/* Enable maj_sel in CRUSH_CTRL (Set bit 20) */
|
||||
cx18_av_and_or4(cx, CXADEC_CRUSH_CTRL, ~0, 0x00500000);
|
||||
|
||||
/* Set VGA_TRACK_RANGE to 0x20 */
|
||||
cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000);
|
||||
|
||||
/* Enable VBI capture */
|
||||
cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F);
|
||||
/* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */
|
||||
|
||||
/* Set the video input.
|
||||
The setting in MODE_CTRL gets lost when we do the above setup */
|
||||
/* EncSetSignalStd(dwDevNum, pEnc->dwSigStd); */
|
||||
/* EncSetVideoInput(dwDevNum, pEnc->VidIndSelection); */
|
||||
|
||||
v = cx18_av_read4(cx, CXADEC_AFE_CTRL);
|
||||
v &= 0xFFFBFFFF; /* turn OFF bit 18 for droop_comp_ch1 */
|
||||
v &= 0xFFFF7FFF; /* turn OFF bit 9 for clamp_sel_ch1 */
|
||||
v &= 0xFFFFFFFE; /* turn OFF bit 0 for 12db_ch1 */
|
||||
/* v |= 0x00000001;*/ /* turn ON bit 0 for 12db_ch1 */
|
||||
cx18_av_write4(cx, CXADEC_AFE_CTRL, v);
|
||||
|
||||
/* if(dwEnable && dw3DCombAvailable) { */
|
||||
/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x7728021F); */
|
||||
/* } else { */
|
||||
/* CxDevWrReg(CXADEC_SRC_COMB_CFG, 0x6628021F); */
|
||||
/* } */
|
||||
cx18_av_write4(cx, CXADEC_SRC_COMB_CFG, 0x6628021F);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static void input_change(struct cx18 *cx)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
v4l2_std_id std = state->std;
|
||||
|
||||
/* Follow step 8c and 8d of section 3.16 in the cx18_av datasheet */
|
||||
if (std & V4L2_STD_SECAM)
|
||||
cx18_av_write(cx, 0x402, 0);
|
||||
else {
|
||||
cx18_av_write(cx, 0x402, 0x04);
|
||||
cx18_av_write(cx, 0x49f, (std & V4L2_STD_NTSC) ? 0x14 : 0x11);
|
||||
}
|
||||
cx18_av_and_or(cx, 0x401, ~0x60, 0);
|
||||
cx18_av_and_or(cx, 0x401, ~0x60, 0x60);
|
||||
|
||||
if (std & V4L2_STD_525_60) {
|
||||
if (std == V4L2_STD_NTSC_M_JP) {
|
||||
/* Japan uses EIAJ audio standard */
|
||||
cx18_av_write(cx, 0x808, 0xf7);
|
||||
} else if (std == V4L2_STD_NTSC_M_KR) {
|
||||
/* South Korea uses A2 audio standard */
|
||||
cx18_av_write(cx, 0x808, 0xf8);
|
||||
} else {
|
||||
/* Others use the BTSC audio standard */
|
||||
cx18_av_write(cx, 0x808, 0xf6);
|
||||
}
|
||||
cx18_av_write(cx, 0x80b, 0x00);
|
||||
} else if (std & V4L2_STD_PAL) {
|
||||
/* Follow tuner change procedure for PAL */
|
||||
cx18_av_write(cx, 0x808, 0xff);
|
||||
cx18_av_write(cx, 0x80b, 0x03);
|
||||
} else if (std & V4L2_STD_SECAM) {
|
||||
/* Select autodetect for SECAM */
|
||||
cx18_av_write(cx, 0x808, 0xff);
|
||||
cx18_av_write(cx, 0x80b, 0x03);
|
||||
}
|
||||
|
||||
if (cx18_av_read(cx, 0x803) & 0x10) {
|
||||
/* restart audio decoder microcontroller */
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0x00);
|
||||
cx18_av_and_or(cx, 0x803, ~0x10, 0x10);
|
||||
}
|
||||
}
|
||||
|
||||
static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
|
||||
enum cx18_av_audio_input aud_input)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
|
||||
vid_input <= CX18_AV_COMPOSITE8);
|
||||
u8 reg;
|
||||
|
||||
CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
|
||||
vid_input, aud_input);
|
||||
|
||||
if (is_composite) {
|
||||
reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1);
|
||||
} else {
|
||||
int luma = vid_input & 0xf0;
|
||||
int chroma = vid_input & 0xf00;
|
||||
|
||||
if ((vid_input & ~0xff0) ||
|
||||
luma < CX18_AV_SVIDEO_LUMA1 ||
|
||||
luma > CX18_AV_SVIDEO_LUMA4 ||
|
||||
chroma < CX18_AV_SVIDEO_CHROMA4 ||
|
||||
chroma > CX18_AV_SVIDEO_CHROMA8) {
|
||||
CX18_ERR("0x%04x is not a valid video input!\n",
|
||||
vid_input);
|
||||
return -EINVAL;
|
||||
}
|
||||
reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4);
|
||||
if (chroma >= CX18_AV_SVIDEO_CHROMA7) {
|
||||
reg &= 0x3f;
|
||||
reg |= (chroma - CX18_AV_SVIDEO_CHROMA7) >> 2;
|
||||
} else {
|
||||
reg &= 0xcf;
|
||||
reg |= (chroma - CX18_AV_SVIDEO_CHROMA4) >> 4;
|
||||
}
|
||||
}
|
||||
|
||||
switch (aud_input) {
|
||||
case CX18_AV_AUDIO_SERIAL:
|
||||
/* do nothing, use serial audio input */
|
||||
break;
|
||||
case CX18_AV_AUDIO4: reg &= ~0x30; break;
|
||||
case CX18_AV_AUDIO5: reg &= ~0x30; reg |= 0x10; break;
|
||||
case CX18_AV_AUDIO6: reg &= ~0x30; reg |= 0x20; break;
|
||||
case CX18_AV_AUDIO7: reg &= ~0xc0; break;
|
||||
case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
|
||||
|
||||
default:
|
||||
CX18_ERR("0x%04x is not a valid audio input!\n", aud_input);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cx18_av_write(cx, 0x103, reg);
|
||||
/* Set INPUT_MODE to Composite (0) or S-Video (1) */
|
||||
cx18_av_and_or(cx, 0x401, ~0x6, is_composite ? 0 : 0x02);
|
||||
/* Set CH_SEL_ADC2 to 1 if input comes from CH3 */
|
||||
cx18_av_and_or(cx, 0x102, ~0x2, (reg & 0x80) == 0 ? 2 : 0);
|
||||
/* Set DUAL_MODE_ADC2 to 1 if input comes from both CH2 and CH3 */
|
||||
if ((reg & 0xc0) != 0xc0 && (reg & 0x30) != 0x30)
|
||||
cx18_av_and_or(cx, 0x102, ~0x4, 4);
|
||||
else
|
||||
cx18_av_and_or(cx, 0x102, ~0x4, 0);
|
||||
/*cx18_av_and_or4(cx, 0x104, ~0x001b4180, 0x00004180);*/
|
||||
|
||||
state->vid_input = vid_input;
|
||||
state->aud_input = aud_input;
|
||||
cx18_av_audio_set_path(cx);
|
||||
input_change(cx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static int set_v4lstd(struct cx18 *cx)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
u8 fmt = 0; /* zero is autodetect */
|
||||
u8 pal_m = 0;
|
||||
|
||||
/* First tests should be against specific std */
|
||||
if (state->std == V4L2_STD_NTSC_M_JP) {
|
||||
fmt = 0x2;
|
||||
} else if (state->std == V4L2_STD_NTSC_443) {
|
||||
fmt = 0x3;
|
||||
} else if (state->std == V4L2_STD_PAL_M) {
|
||||
pal_m = 1;
|
||||
fmt = 0x5;
|
||||
} else if (state->std == V4L2_STD_PAL_N) {
|
||||
fmt = 0x6;
|
||||
} else if (state->std == V4L2_STD_PAL_Nc) {
|
||||
fmt = 0x7;
|
||||
} else if (state->std == V4L2_STD_PAL_60) {
|
||||
fmt = 0x8;
|
||||
} else {
|
||||
/* Then, test against generic ones */
|
||||
if (state->std & V4L2_STD_NTSC)
|
||||
fmt = 0x1;
|
||||
else if (state->std & V4L2_STD_PAL)
|
||||
fmt = 0x4;
|
||||
else if (state->std & V4L2_STD_SECAM)
|
||||
fmt = 0xc;
|
||||
}
|
||||
|
||||
CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt);
|
||||
|
||||
/* Follow step 9 of section 3.16 in the cx18_av datasheet.
|
||||
Without this PAL may display a vertical ghosting effect.
|
||||
This happens for example with the Yuan MPC622. */
|
||||
if (fmt >= 4 && fmt < 8) {
|
||||
/* Set format to NTSC-M */
|
||||
cx18_av_and_or(cx, 0x400, ~0xf, 1);
|
||||
/* Turn off LCOMB */
|
||||
cx18_av_and_or(cx, 0x47b, ~6, 0);
|
||||
}
|
||||
cx18_av_and_or(cx, 0x400, ~0xf, fmt);
|
||||
cx18_av_and_or(cx, 0x403, ~0x3, pal_m);
|
||||
cx18_av_vbi_setup(cx);
|
||||
input_change(cx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
|
||||
{
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
if (ctrl->value < 0 || ctrl->value > 255) {
|
||||
CX18_ERR("invalid brightness setting %d\n",
|
||||
ctrl->value);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
cx18_av_write(cx, 0x414, ctrl->value - 128);
|
||||
break;
|
||||
|
||||
case V4L2_CID_CONTRAST:
|
||||
if (ctrl->value < 0 || ctrl->value > 127) {
|
||||
CX18_ERR("invalid contrast setting %d\n",
|
||||
ctrl->value);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
cx18_av_write(cx, 0x415, ctrl->value << 1);
|
||||
break;
|
||||
|
||||
case V4L2_CID_SATURATION:
|
||||
if (ctrl->value < 0 || ctrl->value > 127) {
|
||||
CX18_ERR("invalid saturation setting %d\n",
|
||||
ctrl->value);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
cx18_av_write(cx, 0x420, ctrl->value << 1);
|
||||
cx18_av_write(cx, 0x421, ctrl->value << 1);
|
||||
break;
|
||||
|
||||
case V4L2_CID_HUE:
|
||||
if (ctrl->value < -127 || ctrl->value > 127) {
|
||||
CX18_ERR("invalid hue setting %d\n", ctrl->value);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
cx18_av_write(cx, 0x422, ctrl->value);
|
||||
break;
|
||||
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
|
||||
{
|
||||
switch (ctrl->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128;
|
||||
break;
|
||||
case V4L2_CID_CONTRAST:
|
||||
ctrl->value = cx18_av_read(cx, 0x415) >> 1;
|
||||
break;
|
||||
case V4L2_CID_SATURATION:
|
||||
ctrl->value = cx18_av_read(cx, 0x420) >> 1;
|
||||
break;
|
||||
case V4L2_CID_HUE:
|
||||
ctrl->value = (s8)cx18_av_read(cx, 0x422);
|
||||
break;
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
|
||||
{
|
||||
switch (fmt->type) {
|
||||
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
|
||||
return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
struct v4l2_pix_format *pix;
|
||||
int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
|
||||
int is_50Hz = !(state->std & V4L2_STD_525_60);
|
||||
|
||||
switch (fmt->type) {
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
||||
pix = &(fmt->fmt.pix);
|
||||
|
||||
Vsrc = (cx18_av_read(cx, 0x476) & 0x3f) << 4;
|
||||
Vsrc |= (cx18_av_read(cx, 0x475) & 0xf0) >> 4;
|
||||
|
||||
Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
|
||||
Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
|
||||
|
||||
Vlines = pix->height + (is_50Hz ? 4 : 7);
|
||||
|
||||
if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
|
||||
(Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
|
||||
CX18_ERR("%dx%d is not a valid size!\n",
|
||||
pix->width, pix->height);
|
||||
return -ERANGE;
|
||||
}
|
||||
|
||||
HSC = (Hsrc * (1 << 20)) / pix->width - (1 << 20);
|
||||
VSC = (1 << 16) - (Vsrc * (1 << 9) / Vlines - (1 << 9));
|
||||
VSC &= 0x1fff;
|
||||
|
||||
if (pix->width >= 385)
|
||||
filter = 0;
|
||||
else if (pix->width > 192)
|
||||
filter = 1;
|
||||
else if (pix->width > 96)
|
||||
filter = 2;
|
||||
else
|
||||
filter = 3;
|
||||
|
||||
CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n",
|
||||
pix->width, pix->height, HSC, VSC);
|
||||
|
||||
/* HSCALE=HSC */
|
||||
cx18_av_write(cx, 0x418, HSC & 0xff);
|
||||
cx18_av_write(cx, 0x419, (HSC >> 8) & 0xff);
|
||||
cx18_av_write(cx, 0x41a, HSC >> 16);
|
||||
/* VSCALE=VSC */
|
||||
cx18_av_write(cx, 0x41c, VSC & 0xff);
|
||||
cx18_av_write(cx, 0x41d, VSC >> 8);
|
||||
/* VS_INTRLACE=1 VFILT=filter */
|
||||
cx18_av_write(cx, 0x41e, 0x8 | filter);
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
|
||||
return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
|
||||
|
||||
case V4L2_BUF_TYPE_VBI_CAPTURE:
|
||||
return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
struct v4l2_tuner *vt = arg;
|
||||
struct v4l2_routing *route = arg;
|
||||
|
||||
/* ignore these commands */
|
||||
switch (cmd) {
|
||||
case TUNER_SET_TYPE_ADDR:
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!state->is_initialized) {
|
||||
CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd);
|
||||
/* initialize on first use */
|
||||
state->is_initialized = 1;
|
||||
cx18_av_initialize(cx);
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VIDIOC_INT_DECODE_VBI_LINE:
|
||||
return cx18_av_vbi(cx, cmd, arg);
|
||||
|
||||
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
|
||||
return cx18_av_audio(cx, cmd, arg);
|
||||
|
||||
case VIDIOC_STREAMON:
|
||||
CX18_DEBUG_INFO("enable output\n");
|
||||
cx18_av_write(cx, 0x115, 0x8c);
|
||||
cx18_av_write(cx, 0x116, 0x07);
|
||||
break;
|
||||
|
||||
case VIDIOC_STREAMOFF:
|
||||
CX18_DEBUG_INFO("disable output\n");
|
||||
cx18_av_write(cx, 0x115, 0x00);
|
||||
cx18_av_write(cx, 0x116, 0x00);
|
||||
break;
|
||||
|
||||
case VIDIOC_LOG_STATUS:
|
||||
log_video_status(cx);
|
||||
log_audio_status(cx);
|
||||
break;
|
||||
|
||||
case VIDIOC_G_CTRL:
|
||||
return get_v4lctrl(cx, (struct v4l2_control *)arg);
|
||||
|
||||
case VIDIOC_S_CTRL:
|
||||
return set_v4lctrl(cx, (struct v4l2_control *)arg);
|
||||
|
||||
case VIDIOC_QUERYCTRL:
|
||||
{
|
||||
struct v4l2_queryctrl *qc = arg;
|
||||
|
||||
switch (qc->id) {
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
case V4L2_CID_CONTRAST:
|
||||
case V4L2_CID_SATURATION:
|
||||
case V4L2_CID_HUE:
|
||||
return v4l2_ctrl_query_fill_std(qc);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch (qc->id) {
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
return v4l2_ctrl_query_fill_std(qc);
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case VIDIOC_G_STD:
|
||||
*(v4l2_std_id *)arg = state->std;
|
||||
break;
|
||||
|
||||
case VIDIOC_S_STD:
|
||||
if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
|
||||
return 0;
|
||||
state->radio = 0;
|
||||
state->std = *(v4l2_std_id *)arg;
|
||||
return set_v4lstd(cx);
|
||||
|
||||
case AUDC_SET_RADIO:
|
||||
state->radio = 1;
|
||||
break;
|
||||
|
||||
case VIDIOC_INT_G_VIDEO_ROUTING:
|
||||
route->input = state->vid_input;
|
||||
route->output = 0;
|
||||
break;
|
||||
|
||||
case VIDIOC_INT_S_VIDEO_ROUTING:
|
||||
return set_input(cx, route->input, state->aud_input);
|
||||
|
||||
case VIDIOC_INT_G_AUDIO_ROUTING:
|
||||
route->input = state->aud_input;
|
||||
route->output = 0;
|
||||
break;
|
||||
|
||||
case VIDIOC_INT_S_AUDIO_ROUTING:
|
||||
return set_input(cx, state->vid_input, route->input);
|
||||
|
||||
case VIDIOC_S_FREQUENCY:
|
||||
input_change(cx);
|
||||
break;
|
||||
|
||||
case VIDIOC_G_TUNER:
|
||||
{
|
||||
u8 vpres = cx18_av_read(cx, 0x40e) & 0x20;
|
||||
u8 mode;
|
||||
int val = 0;
|
||||
|
||||
if (state->radio)
|
||||
break;
|
||||
|
||||
vt->signal = vpres ? 0xffff : 0x0;
|
||||
|
||||
vt->capability |=
|
||||
V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
|
||||
V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
|
||||
|
||||
mode = cx18_av_read(cx, 0x804);
|
||||
|
||||
/* get rxsubchans and audmode */
|
||||
if ((mode & 0xf) == 1)
|
||||
val |= V4L2_TUNER_SUB_STEREO;
|
||||
else
|
||||
val |= V4L2_TUNER_SUB_MONO;
|
||||
|
||||
if (mode == 2 || mode == 4)
|
||||
val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
|
||||
|
||||
if (mode & 0x10)
|
||||
val |= V4L2_TUNER_SUB_SAP;
|
||||
|
||||
vt->rxsubchans = val;
|
||||
vt->audmode = state->audmode;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_S_TUNER:
|
||||
if (state->radio)
|
||||
break;
|
||||
|
||||
switch (vt->audmode) {
|
||||
case V4L2_TUNER_MODE_MONO:
|
||||
/* mono -> mono
|
||||
stereo -> mono
|
||||
bilingual -> lang1 */
|
||||
cx18_av_and_or(cx, 0x809, ~0xf, 0x00);
|
||||
break;
|
||||
case V4L2_TUNER_MODE_STEREO:
|
||||
case V4L2_TUNER_MODE_LANG1:
|
||||
/* mono -> mono
|
||||
stereo -> stereo
|
||||
bilingual -> lang1 */
|
||||
cx18_av_and_or(cx, 0x809, ~0xf, 0x04);
|
||||
break;
|
||||
case V4L2_TUNER_MODE_LANG1_LANG2:
|
||||
/* mono -> mono
|
||||
stereo -> stereo
|
||||
bilingual -> lang1/lang2 */
|
||||
cx18_av_and_or(cx, 0x809, ~0xf, 0x07);
|
||||
break;
|
||||
case V4L2_TUNER_MODE_LANG2:
|
||||
/* mono -> mono
|
||||
stereo -> stereo
|
||||
bilingual -> lang2 */
|
||||
cx18_av_and_or(cx, 0x809, ~0xf, 0x01);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
state->audmode = vt->audmode;
|
||||
break;
|
||||
|
||||
case VIDIOC_G_FMT:
|
||||
return get_v4lfmt(cx, (struct v4l2_format *)arg);
|
||||
|
||||
case VIDIOC_S_FMT:
|
||||
return set_v4lfmt(cx, (struct v4l2_format *)arg);
|
||||
|
||||
case VIDIOC_INT_RESET:
|
||||
cx18_av_initialize(cx);
|
||||
break;
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static void log_video_status(struct cx18 *cx)
|
||||
{
|
||||
static const char *const fmt_strs[] = {
|
||||
"0x0",
|
||||
"NTSC-M", "NTSC-J", "NTSC-4.43",
|
||||
"PAL-BDGHI", "PAL-M", "PAL-N", "PAL-Nc", "PAL-60",
|
||||
"0x9", "0xA", "0xB",
|
||||
"SECAM",
|
||||
"0xD", "0xE", "0xF"
|
||||
};
|
||||
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf;
|
||||
u8 gen_stat1 = cx18_av_read(cx, 0x40d);
|
||||
u8 gen_stat2 = cx18_av_read(cx, 0x40e);
|
||||
int vid_input = state->vid_input;
|
||||
|
||||
CX18_INFO("Video signal: %spresent\n",
|
||||
(gen_stat2 & 0x20) ? "" : "not ");
|
||||
CX18_INFO("Detected format: %s\n",
|
||||
fmt_strs[gen_stat1 & 0xf]);
|
||||
|
||||
CX18_INFO("Specified standard: %s\n",
|
||||
vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
|
||||
|
||||
if (vid_input >= CX18_AV_COMPOSITE1 &&
|
||||
vid_input <= CX18_AV_COMPOSITE8) {
|
||||
CX18_INFO("Specified video input: Composite %d\n",
|
||||
vid_input - CX18_AV_COMPOSITE1 + 1);
|
||||
} else {
|
||||
CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
|
||||
(vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
|
||||
}
|
||||
|
||||
CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq);
|
||||
}
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
|
||||
static void log_audio_status(struct cx18 *cx)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
u8 download_ctl = cx18_av_read(cx, 0x803);
|
||||
u8 mod_det_stat0 = cx18_av_read(cx, 0x805);
|
||||
u8 mod_det_stat1 = cx18_av_read(cx, 0x804);
|
||||
u8 audio_config = cx18_av_read(cx, 0x808);
|
||||
u8 pref_mode = cx18_av_read(cx, 0x809);
|
||||
u8 afc0 = cx18_av_read(cx, 0x80b);
|
||||
u8 mute_ctl = cx18_av_read(cx, 0x8d3);
|
||||
int aud_input = state->aud_input;
|
||||
char *p;
|
||||
|
||||
switch (mod_det_stat0) {
|
||||
case 0x00: p = "mono"; break;
|
||||
case 0x01: p = "stereo"; break;
|
||||
case 0x02: p = "dual"; break;
|
||||
case 0x04: p = "tri"; break;
|
||||
case 0x10: p = "mono with SAP"; break;
|
||||
case 0x11: p = "stereo with SAP"; break;
|
||||
case 0x12: p = "dual with SAP"; break;
|
||||
case 0x14: p = "tri with SAP"; break;
|
||||
case 0xfe: p = "forced mode"; break;
|
||||
default: p = "not defined";
|
||||
}
|
||||
CX18_INFO("Detected audio mode: %s\n", p);
|
||||
|
||||
switch (mod_det_stat1) {
|
||||
case 0x00: p = "BTSC"; break;
|
||||
case 0x01: p = "EIAJ"; break;
|
||||
case 0x02: p = "A2-M"; break;
|
||||
case 0x03: p = "A2-BG"; break;
|
||||
case 0x04: p = "A2-DK1"; break;
|
||||
case 0x05: p = "A2-DK2"; break;
|
||||
case 0x06: p = "A2-DK3"; break;
|
||||
case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
|
||||
case 0x08: p = "AM-L"; break;
|
||||
case 0x09: p = "NICAM-BG"; break;
|
||||
case 0x0a: p = "NICAM-DK"; break;
|
||||
case 0x0b: p = "NICAM-I"; break;
|
||||
case 0x0c: p = "NICAM-L"; break;
|
||||
case 0x0d: p = "BTSC/EIAJ/A2-M Mono (4.5 MHz FMMono)"; break;
|
||||
case 0xff: p = "no detected audio standard"; break;
|
||||
default: p = "not defined";
|
||||
}
|
||||
CX18_INFO("Detected audio standard: %s\n", p);
|
||||
CX18_INFO("Audio muted: %s\n",
|
||||
(mute_ctl & 0x2) ? "yes" : "no");
|
||||
CX18_INFO("Audio microcontroller: %s\n",
|
||||
(download_ctl & 0x10) ? "running" : "stopped");
|
||||
|
||||
switch (audio_config >> 4) {
|
||||
case 0x00: p = "BTSC"; break;
|
||||
case 0x01: p = "EIAJ"; break;
|
||||
case 0x02: p = "A2-M"; break;
|
||||
case 0x03: p = "A2-BG"; break;
|
||||
case 0x04: p = "A2-DK1"; break;
|
||||
case 0x05: p = "A2-DK2"; break;
|
||||
case 0x06: p = "A2-DK3"; break;
|
||||
case 0x07: p = "A1 (6.0 MHz FM Mono)"; break;
|
||||
case 0x08: p = "AM-L"; break;
|
||||
case 0x09: p = "NICAM-BG"; break;
|
||||
case 0x0a: p = "NICAM-DK"; break;
|
||||
case 0x0b: p = "NICAM-I"; break;
|
||||
case 0x0c: p = "NICAM-L"; break;
|
||||
case 0x0d: p = "FM radio"; break;
|
||||
case 0x0f: p = "automatic detection"; break;
|
||||
default: p = "undefined";
|
||||
}
|
||||
CX18_INFO("Configured audio standard: %s\n", p);
|
||||
|
||||
if ((audio_config >> 4) < 0xF) {
|
||||
switch (audio_config & 0xF) {
|
||||
case 0x00: p = "MONO1 (LANGUAGE A/Mono L+R channel for BTSC, EIAJ, A2)"; break;
|
||||
case 0x01: p = "MONO2 (LANGUAGE B)"; break;
|
||||
case 0x02: p = "MONO3 (STEREO forced MONO)"; break;
|
||||
case 0x03: p = "MONO4 (NICAM ANALOG-Language C/Analog Fallback)"; break;
|
||||
case 0x04: p = "STEREO"; break;
|
||||
case 0x05: p = "DUAL1 (AB)"; break;
|
||||
case 0x06: p = "DUAL2 (AC) (FM)"; break;
|
||||
case 0x07: p = "DUAL3 (BC) (FM)"; break;
|
||||
case 0x08: p = "DUAL4 (AC) (AM)"; break;
|
||||
case 0x09: p = "DUAL5 (BC) (AM)"; break;
|
||||
case 0x0a: p = "SAP"; break;
|
||||
default: p = "undefined";
|
||||
}
|
||||
CX18_INFO("Configured audio mode: %s\n", p);
|
||||
} else {
|
||||
switch (audio_config & 0xF) {
|
||||
case 0x00: p = "BG"; break;
|
||||
case 0x01: p = "DK1"; break;
|
||||
case 0x02: p = "DK2"; break;
|
||||
case 0x03: p = "DK3"; break;
|
||||
case 0x04: p = "I"; break;
|
||||
case 0x05: p = "L"; break;
|
||||
case 0x06: p = "BTSC"; break;
|
||||
case 0x07: p = "EIAJ"; break;
|
||||
case 0x08: p = "A2-M"; break;
|
||||
case 0x09: p = "FM Radio"; break;
|
||||
case 0x0f: p = "automatic standard and mode detection"; break;
|
||||
default: p = "undefined";
|
||||
}
|
||||
CX18_INFO("Configured audio system: %s\n", p);
|
||||
}
|
||||
|
||||
if (aud_input)
|
||||
CX18_INFO("Specified audio input: Tuner (In%d)\n",
|
||||
aud_input);
|
||||
else
|
||||
CX18_INFO("Specified audio input: External\n");
|
||||
|
||||
switch (pref_mode & 0xf) {
|
||||
case 0: p = "mono/language A"; break;
|
||||
case 1: p = "language B"; break;
|
||||
case 2: p = "language C"; break;
|
||||
case 3: p = "analog fallback"; break;
|
||||
case 4: p = "stereo"; break;
|
||||
case 5: p = "language AC"; break;
|
||||
case 6: p = "language BC"; break;
|
||||
case 7: p = "language AB"; break;
|
||||
default: p = "undefined";
|
||||
}
|
||||
CX18_INFO("Preferred audio mode: %s\n", p);
|
||||
|
||||
if ((audio_config & 0xf) == 0xf) {
|
||||
switch ((afc0 >> 2) & 0x1) {
|
||||
case 0: p = "system DK"; break;
|
||||
case 1: p = "system L"; break;
|
||||
}
|
||||
CX18_INFO("Selected 65 MHz format: %s\n", p);
|
||||
|
||||
switch (afc0 & 0x3) {
|
||||
case 0: p = "BTSC"; break;
|
||||
case 1: p = "EIAJ"; break;
|
||||
case 2: p = "A2-M"; break;
|
||||
default: p = "undefined";
|
||||
}
|
||||
CX18_INFO("Selected 45 MHz format: %s\n", p);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
/*
|
||||
* cx18 ADEC header
|
||||
*
|
||||
* Derived from cx25840-core.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef _CX18_AV_CORE_H_
|
||||
#define _CX18_AV_CORE_H_
|
||||
|
||||
struct cx18;
|
||||
|
||||
enum cx18_av_video_input {
|
||||
/* Composite video inputs In1-In8 */
|
||||
CX18_AV_COMPOSITE1 = 1,
|
||||
CX18_AV_COMPOSITE2,
|
||||
CX18_AV_COMPOSITE3,
|
||||
CX18_AV_COMPOSITE4,
|
||||
CX18_AV_COMPOSITE5,
|
||||
CX18_AV_COMPOSITE6,
|
||||
CX18_AV_COMPOSITE7,
|
||||
CX18_AV_COMPOSITE8,
|
||||
|
||||
/* S-Video inputs consist of one luma input (In1-In4) ORed with one
|
||||
chroma input (In5-In8) */
|
||||
CX18_AV_SVIDEO_LUMA1 = 0x10,
|
||||
CX18_AV_SVIDEO_LUMA2 = 0x20,
|
||||
CX18_AV_SVIDEO_LUMA3 = 0x30,
|
||||
CX18_AV_SVIDEO_LUMA4 = 0x40,
|
||||
CX18_AV_SVIDEO_CHROMA4 = 0x400,
|
||||
CX18_AV_SVIDEO_CHROMA5 = 0x500,
|
||||
CX18_AV_SVIDEO_CHROMA6 = 0x600,
|
||||
CX18_AV_SVIDEO_CHROMA7 = 0x700,
|
||||
CX18_AV_SVIDEO_CHROMA8 = 0x800,
|
||||
|
||||
/* S-Video aliases for common luma/chroma combinations */
|
||||
CX18_AV_SVIDEO1 = 0x510,
|
||||
CX18_AV_SVIDEO2 = 0x620,
|
||||
CX18_AV_SVIDEO3 = 0x730,
|
||||
CX18_AV_SVIDEO4 = 0x840,
|
||||
};
|
||||
|
||||
enum cx18_av_audio_input {
|
||||
/* Audio inputs: serial or In4-In8 */
|
||||
CX18_AV_AUDIO_SERIAL,
|
||||
CX18_AV_AUDIO4 = 4,
|
||||
CX18_AV_AUDIO5,
|
||||
CX18_AV_AUDIO6,
|
||||
CX18_AV_AUDIO7,
|
||||
CX18_AV_AUDIO8,
|
||||
};
|
||||
|
||||
struct cx18_av_state {
|
||||
int radio;
|
||||
v4l2_std_id std;
|
||||
enum cx18_av_video_input vid_input;
|
||||
enum cx18_av_audio_input aud_input;
|
||||
u32 audclk_freq;
|
||||
int audmode;
|
||||
int vbi_line_offset;
|
||||
u32 id;
|
||||
u32 rev;
|
||||
int is_initialized;
|
||||
};
|
||||
|
||||
|
||||
/* Registers */
|
||||
#define CXADEC_CHIP_TYPE_TIGER 0x837
|
||||
#define CXADEC_CHIP_TYPE_MAKO 0x843
|
||||
|
||||
#define CXADEC_HOST_REG1 0x000
|
||||
#define CXADEC_HOST_REG2 0x001
|
||||
|
||||
#define CXADEC_CHIP_CTRL 0x100
|
||||
#define CXADEC_AFE_CTRL 0x104
|
||||
#define CXADEC_PLL_CTRL1 0x108
|
||||
#define CXADEC_VID_PLL_FRAC 0x10C
|
||||
#define CXADEC_AUX_PLL_FRAC 0x110
|
||||
#define CXADEC_PIN_CTRL1 0x114
|
||||
#define CXADEC_PIN_CTRL2 0x118
|
||||
#define CXADEC_PIN_CFG1 0x11C
|
||||
#define CXADEC_PIN_CFG2 0x120
|
||||
|
||||
#define CXADEC_PIN_CFG3 0x124
|
||||
#define CXADEC_I2S_MCLK 0x127
|
||||
|
||||
#define CXADEC_AUD_LOCK1 0x128
|
||||
#define CXADEC_AUD_LOCK2 0x12C
|
||||
#define CXADEC_POWER_CTRL 0x130
|
||||
#define CXADEC_AFE_DIAG_CTRL1 0x134
|
||||
#define CXADEC_AFE_DIAG_CTRL2 0x138
|
||||
#define CXADEC_AFE_DIAG_CTRL3 0x13C
|
||||
#define CXADEC_PLL_DIAG_CTRL 0x140
|
||||
#define CXADEC_TEST_CTRL1 0x144
|
||||
#define CXADEC_TEST_CTRL2 0x148
|
||||
#define CXADEC_BIST_STAT 0x14C
|
||||
#define CXADEC_DLL1_DIAG_CTRL 0x158
|
||||
#define CXADEC_DLL2_DIAG_CTRL 0x15C
|
||||
|
||||
/* IR registers */
|
||||
#define CXADEC_IR_CTRL_REG 0x200
|
||||
#define CXADEC_IR_TXCLK_REG 0x204
|
||||
#define CXADEC_IR_RXCLK_REG 0x208
|
||||
#define CXADEC_IR_CDUTY_REG 0x20C
|
||||
#define CXADEC_IR_STAT_REG 0x210
|
||||
#define CXADEC_IR_IRQEN_REG 0x214
|
||||
#define CXADEC_IR_FILTER_REG 0x218
|
||||
#define CXADEC_IR_FIFO_REG 0x21C
|
||||
|
||||
/* Video Registers */
|
||||
#define CXADEC_MODE_CTRL 0x400
|
||||
#define CXADEC_OUT_CTRL1 0x404
|
||||
#define CXADEC_OUT_CTRL2 0x408
|
||||
#define CXADEC_GEN_STAT 0x40C
|
||||
#define CXADEC_INT_STAT_MASK 0x410
|
||||
#define CXADEC_LUMA_CTRL 0x414
|
||||
|
||||
#define CXADEC_BRIGHTNESS_CTRL_BYTE 0x414
|
||||
#define CXADEC_CONTRAST_CTRL_BYTE 0x415
|
||||
#define CXADEC_LUMA_CTRL_BYTE_3 0x416
|
||||
|
||||
#define CXADEC_HSCALE_CTRL 0x418
|
||||
#define CXADEC_VSCALE_CTRL 0x41C
|
||||
|
||||
#define CXADEC_CHROMA_CTRL 0x420
|
||||
|
||||
#define CXADEC_USAT_CTRL_BYTE 0x420
|
||||
#define CXADEC_VSAT_CTRL_BYTE 0x421
|
||||
#define CXADEC_HUE_CTRL_BYTE 0x422
|
||||
|
||||
#define CXADEC_VBI_LINE_CTRL1 0x424
|
||||
#define CXADEC_VBI_LINE_CTRL2 0x428
|
||||
#define CXADEC_VBI_LINE_CTRL3 0x42C
|
||||
#define CXADEC_VBI_LINE_CTRL4 0x430
|
||||
#define CXADEC_VBI_LINE_CTRL5 0x434
|
||||
#define CXADEC_VBI_FC_CFG 0x438
|
||||
#define CXADEC_VBI_MISC_CFG1 0x43C
|
||||
#define CXADEC_VBI_MISC_CFG2 0x440
|
||||
#define CXADEC_VBI_PAY1 0x444
|
||||
#define CXADEC_VBI_PAY2 0x448
|
||||
#define CXADEC_VBI_CUST1_CFG1 0x44C
|
||||
#define CXADEC_VBI_CUST1_CFG2 0x450
|
||||
#define CXADEC_VBI_CUST1_CFG3 0x454
|
||||
#define CXADEC_VBI_CUST2_CFG1 0x458
|
||||
#define CXADEC_VBI_CUST2_CFG2 0x45C
|
||||
#define CXADEC_VBI_CUST2_CFG3 0x460
|
||||
#define CXADEC_VBI_CUST3_CFG1 0x464
|
||||
#define CXADEC_VBI_CUST3_CFG2 0x468
|
||||
#define CXADEC_VBI_CUST3_CFG3 0x46C
|
||||
#define CXADEC_HORIZ_TIM_CTRL 0x470
|
||||
#define CXADEC_VERT_TIM_CTRL 0x474
|
||||
#define CXADEC_SRC_COMB_CFG 0x478
|
||||
#define CXADEC_CHROMA_VBIOFF_CFG 0x47C
|
||||
#define CXADEC_FIELD_COUNT 0x480
|
||||
#define CXADEC_MISC_TIM_CTRL 0x484
|
||||
#define CXADEC_DFE_CTRL1 0x488
|
||||
#define CXADEC_DFE_CTRL2 0x48C
|
||||
#define CXADEC_DFE_CTRL3 0x490
|
||||
#define CXADEC_PLL_CTRL2 0x494
|
||||
#define CXADEC_HTL_CTRL 0x498
|
||||
#define CXADEC_COMB_CTRL 0x49C
|
||||
#define CXADEC_CRUSH_CTRL 0x4A0
|
||||
#define CXADEC_SOFT_RST_CTRL 0x4A4
|
||||
#define CXADEC_MV_DT_CTRL2 0x4A8
|
||||
#define CXADEC_MV_DT_CTRL3 0x4AC
|
||||
#define CXADEC_MISC_DIAG_CTRL 0x4B8
|
||||
|
||||
#define CXADEC_DL_CTL 0x800
|
||||
#define CXADEC_DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */
|
||||
#define CXADEC_DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */
|
||||
#define CXADEC_DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */
|
||||
#define CXADEC_DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */
|
||||
|
||||
#define CXADEC_STD_DET_STATUS 0x804
|
||||
|
||||
#define CXADEC_STD_DET_CTL 0x808
|
||||
#define CXADEC_STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */
|
||||
#define CXADEC_STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
|
||||
|
||||
#define CXADEC_DW8051_INT 0x80C
|
||||
#define CXADEC_GENERAL_CTL 0x810
|
||||
#define CXADEC_AAGC_CTL 0x814
|
||||
#define CXADEC_IF_SRC_CTL 0x818
|
||||
#define CXADEC_ANLOG_DEMOD_CTL 0x81C
|
||||
#define CXADEC_ROT_FREQ_CTL 0x820
|
||||
#define CXADEC_FM1_CTL 0x824
|
||||
#define CXADEC_PDF_CTL 0x828
|
||||
#define CXADEC_DFT1_CTL1 0x82C
|
||||
#define CXADEC_DFT1_CTL2 0x830
|
||||
#define CXADEC_DFT_STATUS 0x834
|
||||
#define CXADEC_DFT2_CTL1 0x838
|
||||
#define CXADEC_DFT2_CTL2 0x83C
|
||||
#define CXADEC_DFT2_STATUS 0x840
|
||||
#define CXADEC_DFT3_CTL1 0x844
|
||||
#define CXADEC_DFT3_CTL2 0x848
|
||||
#define CXADEC_DFT3_STATUS 0x84C
|
||||
#define CXADEC_DFT4_CTL1 0x850
|
||||
#define CXADEC_DFT4_CTL2 0x854
|
||||
#define CXADEC_DFT4_STATUS 0x858
|
||||
#define CXADEC_AM_MTS_DET 0x85C
|
||||
#define CXADEC_ANALOG_MUX_CTL 0x860
|
||||
#define CXADEC_DIG_PLL_CTL1 0x864
|
||||
#define CXADEC_DIG_PLL_CTL2 0x868
|
||||
#define CXADEC_DIG_PLL_CTL3 0x86C
|
||||
#define CXADEC_DIG_PLL_CTL4 0x870
|
||||
#define CXADEC_DIG_PLL_CTL5 0x874
|
||||
#define CXADEC_DEEMPH_GAIN_CTL 0x878
|
||||
#define CXADEC_DEEMPH_COEF1 0x87C
|
||||
#define CXADEC_DEEMPH_COEF2 0x880
|
||||
#define CXADEC_DBX1_CTL1 0x884
|
||||
#define CXADEC_DBX1_CTL2 0x888
|
||||
#define CXADEC_DBX1_STATUS 0x88C
|
||||
#define CXADEC_DBX2_CTL1 0x890
|
||||
#define CXADEC_DBX2_CTL2 0x894
|
||||
#define CXADEC_DBX2_STATUS 0x898
|
||||
#define CXADEC_AM_FM_DIFF 0x89C
|
||||
|
||||
/* NICAM registers go here */
|
||||
#define CXADEC_NICAM_STATUS 0x8C8
|
||||
#define CXADEC_DEMATRIX_CTL 0x8CC
|
||||
|
||||
#define CXADEC_PATH1_CTL1 0x8D0
|
||||
#define CXADEC_PATH1_VOL_CTL 0x8D4
|
||||
#define CXADEC_PATH1_EQ_CTL 0x8D8
|
||||
#define CXADEC_PATH1_SC_CTL 0x8DC
|
||||
|
||||
#define CXADEC_PATH2_CTL1 0x8E0
|
||||
#define CXADEC_PATH2_VOL_CTL 0x8E4
|
||||
#define CXADEC_PATH2_EQ_CTL 0x8E8
|
||||
#define CXADEC_PATH2_SC_CTL 0x8EC
|
||||
|
||||
#define CXADEC_SRC_CTL 0x8F0
|
||||
#define CXADEC_SRC_LF_COEF 0x8F4
|
||||
#define CXADEC_SRC1_CTL 0x8F8
|
||||
#define CXADEC_SRC2_CTL 0x8FC
|
||||
#define CXADEC_SRC3_CTL 0x900
|
||||
#define CXADEC_SRC4_CTL 0x904
|
||||
#define CXADEC_SRC5_CTL 0x908
|
||||
#define CXADEC_SRC6_CTL 0x90C
|
||||
|
||||
#define CXADEC_BASEBAND_OUT_SEL 0x910
|
||||
#define CXADEC_I2S_IN_CTL 0x914
|
||||
#define CXADEC_I2S_OUT_CTL 0x918
|
||||
#define CXADEC_AC97_CTL 0x91C
|
||||
#define CXADEC_QAM_PDF 0x920
|
||||
#define CXADEC_QAM_CONST_DEC 0x924
|
||||
#define CXADEC_QAM_ROTATOR_FREQ 0x948
|
||||
|
||||
/* Bit defintions / settings used in Mako Audio */
|
||||
#define CXADEC_PREF_MODE_MONO_LANGA 0
|
||||
#define CXADEC_PREF_MODE_MONO_LANGB 1
|
||||
#define CXADEC_PREF_MODE_MONO_LANGC 2
|
||||
#define CXADEC_PREF_MODE_FALLBACK 3
|
||||
#define CXADEC_PREF_MODE_STEREO 4
|
||||
#define CXADEC_PREF_MODE_DUAL_LANG_AC 5
|
||||
#define CXADEC_PREF_MODE_DUAL_LANG_BC 6
|
||||
#define CXADEC_PREF_MODE_DUAL_LANG_AB 7
|
||||
|
||||
|
||||
#define CXADEC_DETECT_STEREO 1
|
||||
#define CXADEC_DETECT_DUAL 2
|
||||
#define CXADEC_DETECT_TRI 4
|
||||
#define CXADEC_DETECT_SAP 0x10
|
||||
#define CXADEC_DETECT_NO_SIGNAL 0xFF
|
||||
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_BG 0xF0 /* NICAM BG and A2 BG */
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_DK1 0xF1 /* NICAM DK and A2 DK */
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_DK2 0xF2
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_DK3 0xF3
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_I 0xF4 /* NICAM I and A1 */
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_L 0xF5 /* NICAM L and System L AM */
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_BTSC 0xF6
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_EIAJ 0xF7
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_A2_M 0xF8 /* A2 M */
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */
|
||||
#define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx18_av-core.c */
|
||||
int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
|
||||
int cx18_av_write4(struct cx18 *cx, u16 addr, u32 value);
|
||||
u8 cx18_av_read(struct cx18 *cx, u16 addr);
|
||||
u32 cx18_av_read4(struct cx18 *cx, u16 addr);
|
||||
int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
|
||||
int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
|
||||
int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx18_av-firmware.c */
|
||||
int cx18_av_loadfw(struct cx18 *cx);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx18_av-audio.c */
|
||||
int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg);
|
||||
void cx18_av_audio_set_path(struct cx18 *cx);
|
||||
|
||||
/* ----------------------------------------------------------------------- */
|
||||
/* cx18_av-vbi.c */
|
||||
void cx18_av_vbi_setup(struct cx18 *cx);
|
||||
int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,120 @@
|
|||
/*
|
||||
* cx18 ADEC firmware functions
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#define FWFILE "v4l-cx23418-dig.fw"
|
||||
|
||||
int cx18_av_loadfw(struct cx18 *cx)
|
||||
{
|
||||
const struct firmware *fw = NULL;
|
||||
u32 size;
|
||||
u32 v;
|
||||
u8 *ptr;
|
||||
int i;
|
||||
|
||||
if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) {
|
||||
CX18_ERR("unable to open firmware %s\n", FWFILE);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cx18_av_write4(cx, CXADEC_CHIP_CTRL, 0x00010000);
|
||||
cx18_av_write(cx, CXADEC_STD_DET_CTL, 0xf6); /* Byte 0 */
|
||||
|
||||
/* Reset the Mako core (Register is undocumented.) */
|
||||
cx18_av_write4(cx, 0x8100, 0x00010000);
|
||||
|
||||
/* Put the 8051 in reset and enable firmware upload */
|
||||
cx18_av_write4(cx, CXADEC_DL_CTL, 0x0F000000);
|
||||
|
||||
ptr = fw->data;
|
||||
size = fw->size;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
u32 dl_control = 0x0F000000 | ((u32)ptr[i] << 16);
|
||||
u32 value = 0;
|
||||
int retries;
|
||||
|
||||
for (retries = 0; retries < 5; retries++) {
|
||||
cx18_av_write4(cx, CXADEC_DL_CTL, dl_control);
|
||||
value = cx18_av_read4(cx, CXADEC_DL_CTL);
|
||||
if ((value & 0x3F00) == (dl_control & 0x3F00))
|
||||
break;
|
||||
}
|
||||
if (retries >= 5) {
|
||||
CX18_ERR("unable to load firmware %s\n", FWFILE);
|
||||
release_firmware(fw);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
cx18_av_write4(cx, CXADEC_DL_CTL, 0x13000000 | fw->size);
|
||||
|
||||
/* Output to the 416 */
|
||||
cx18_av_and_or4(cx, CXADEC_PIN_CTRL1, ~0, 0x78000);
|
||||
|
||||
/* Audio input control 1 set to Sony mode */
|
||||
/* Audio output input 2 is 0 for slave operation input */
|
||||
/* 0xC4000914[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
|
||||
/* 0xC4000914[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
|
||||
after WS transition for first bit of audio word. */
|
||||
cx18_av_write4(cx, CXADEC_I2S_IN_CTL, 0x000000A0);
|
||||
|
||||
/* Audio output control 1 is set to Sony mode */
|
||||
/* Audio output control 2 is set to 1 for master mode */
|
||||
/* 0xC4000918[5]: 0 = left sample on WS=0, 1 = left sample on WS=1 */
|
||||
/* 0xC4000918[7]: 0 = Philips mode, 1 = Sony mode (1st SCK rising edge
|
||||
after WS transition for first bit of audio word. */
|
||||
/* 0xC4000918[8]: 0 = slave operation, 1 = master (SCK_OUT and WS_OUT
|
||||
are generated) */
|
||||
cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
|
||||
|
||||
/* set alt I2s master clock to /16 and enable alt divider i2s
|
||||
passthrough */
|
||||
cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
|
||||
|
||||
cx18_av_write4(cx, CXADEC_STD_DET_CTL, 0x000000F6);
|
||||
/* CxDevWrReg(CXADEC_STD_DET_CTL, 0x000000FF); */
|
||||
|
||||
/* Set bit 0 in register 0x9CC to signify that this is MiniMe. */
|
||||
/* Register 0x09CC is defined by the Merlin firmware, and doesn't
|
||||
have a name in the spec. */
|
||||
cx18_av_write4(cx, 0x09CC, 1);
|
||||
|
||||
#define CX18_AUDIO_ENABLE 0xc72014
|
||||
v = read_reg(CX18_AUDIO_ENABLE);
|
||||
/* If bit 11 is 1 */
|
||||
if (v & 0x800)
|
||||
write_reg(v & 0xFFFFFBFF, CX18_AUDIO_ENABLE); /* Clear bit 10 */
|
||||
|
||||
/* Enable WW auto audio standard detection */
|
||||
v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
|
||||
v |= 0xFF; /* Auto by default */
|
||||
v |= 0x400; /* Stereo by default */
|
||||
v |= 0x14000000;
|
||||
cx18_av_write4(cx, CXADEC_STD_DET_CTL, v);
|
||||
|
||||
release_firmware(fw);
|
||||
|
||||
CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,413 @@
|
|||
/*
|
||||
* cx18 ADEC VBI functions
|
||||
*
|
||||
* Derived from cx25840-vbi.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version 2
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*/
|
||||
|
||||
|
||||
#include "cx18-driver.h"
|
||||
|
||||
static int odd_parity(u8 c)
|
||||
{
|
||||
c ^= (c >> 4);
|
||||
c ^= (c >> 2);
|
||||
c ^= (c >> 1);
|
||||
|
||||
return c & 1;
|
||||
}
|
||||
|
||||
static int decode_vps(u8 *dst, u8 *p)
|
||||
{
|
||||
static const u8 biphase_tbl[] = {
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
|
||||
0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
|
||||
0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
|
||||
0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
|
||||
0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
|
||||
0xc3, 0x4b, 0x43, 0xc3, 0x87, 0x0f, 0x07, 0x87,
|
||||
0x83, 0x0b, 0x03, 0x83, 0xc3, 0x4b, 0x43, 0xc3,
|
||||
0xc1, 0x49, 0x41, 0xc1, 0x85, 0x0d, 0x05, 0x85,
|
||||
0x81, 0x09, 0x01, 0x81, 0xc1, 0x49, 0x41, 0xc1,
|
||||
0xe1, 0x69, 0x61, 0xe1, 0xa5, 0x2d, 0x25, 0xa5,
|
||||
0xa1, 0x29, 0x21, 0xa1, 0xe1, 0x69, 0x61, 0xe1,
|
||||
0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
|
||||
0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
|
||||
0xc2, 0x4a, 0x42, 0xc2, 0x86, 0x0e, 0x06, 0x86,
|
||||
0x82, 0x0a, 0x02, 0x82, 0xc2, 0x4a, 0x42, 0xc2,
|
||||
0xc0, 0x48, 0x40, 0xc0, 0x84, 0x0c, 0x04, 0x84,
|
||||
0x80, 0x08, 0x00, 0x80, 0xc0, 0x48, 0x40, 0xc0,
|
||||
0xe0, 0x68, 0x60, 0xe0, 0xa4, 0x2c, 0x24, 0xa4,
|
||||
0xa0, 0x28, 0x20, 0xa0, 0xe0, 0x68, 0x60, 0xe0,
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
0xd2, 0x5a, 0x52, 0xd2, 0x96, 0x1e, 0x16, 0x96,
|
||||
0x92, 0x1a, 0x12, 0x92, 0xd2, 0x5a, 0x52, 0xd2,
|
||||
0xd0, 0x58, 0x50, 0xd0, 0x94, 0x1c, 0x14, 0x94,
|
||||
0x90, 0x18, 0x10, 0x90, 0xd0, 0x58, 0x50, 0xd0,
|
||||
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
|
||||
0xb0, 0x38, 0x30, 0xb0, 0xf0, 0x78, 0x70, 0xf0,
|
||||
};
|
||||
|
||||
u8 c, err = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 2 * 13; i += 2) {
|
||||
err |= biphase_tbl[p[i]] | biphase_tbl[p[i + 1]];
|
||||
c = (biphase_tbl[p[i + 1]] & 0xf) |
|
||||
((biphase_tbl[p[i]] & 0xf) << 4);
|
||||
dst[i / 2] = c;
|
||||
}
|
||||
|
||||
return err & 0xf0;
|
||||
}
|
||||
|
||||
void cx18_av_vbi_setup(struct cx18 *cx)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
v4l2_std_id std = state->std;
|
||||
int hblank, hactive, burst, vblank, vactive, sc;
|
||||
int vblank656, src_decimation;
|
||||
int luma_lpf, uv_lpf, comb;
|
||||
u32 pll_int, pll_frac, pll_post;
|
||||
|
||||
/* datasheet startup, step 8d */
|
||||
if (std & ~V4L2_STD_NTSC)
|
||||
cx18_av_write(cx, 0x49f, 0x11);
|
||||
else
|
||||
cx18_av_write(cx, 0x49f, 0x14);
|
||||
|
||||
if (std & V4L2_STD_625_50) {
|
||||
hblank = 0x084;
|
||||
hactive = 0x2d0;
|
||||
burst = 0x5d;
|
||||
vblank = 0x024;
|
||||
vactive = 0x244;
|
||||
vblank656 = 0x28;
|
||||
src_decimation = 0x21f;
|
||||
|
||||
luma_lpf = 2;
|
||||
if (std & V4L2_STD_SECAM) {
|
||||
uv_lpf = 0;
|
||||
comb = 0;
|
||||
sc = 0x0a425f;
|
||||
} else if (std == V4L2_STD_PAL_Nc) {
|
||||
uv_lpf = 1;
|
||||
comb = 0x20;
|
||||
sc = 556453;
|
||||
} else {
|
||||
uv_lpf = 1;
|
||||
comb = 0x20;
|
||||
sc = 0x0a8263;
|
||||
}
|
||||
} else {
|
||||
hactive = 720;
|
||||
hblank = 122;
|
||||
vactive = 487;
|
||||
luma_lpf = 1;
|
||||
uv_lpf = 1;
|
||||
|
||||
src_decimation = 0x21f;
|
||||
if (std == V4L2_STD_PAL_60) {
|
||||
vblank = 26;
|
||||
vblank656 = 26;
|
||||
burst = 0x5b;
|
||||
luma_lpf = 2;
|
||||
comb = 0x20;
|
||||
sc = 0x0a8263;
|
||||
} else if (std == V4L2_STD_PAL_M) {
|
||||
vblank = 20;
|
||||
vblank656 = 24;
|
||||
burst = 0x61;
|
||||
comb = 0x20;
|
||||
|
||||
sc = 555452;
|
||||
} else {
|
||||
vblank = 26;
|
||||
vblank656 = 26;
|
||||
burst = 0x5b;
|
||||
comb = 0x66;
|
||||
sc = 556063;
|
||||
}
|
||||
}
|
||||
|
||||
/* DEBUG: Displays configured PLL frequency */
|
||||
pll_int = cx18_av_read(cx, 0x108);
|
||||
pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
|
||||
pll_post = cx18_av_read(cx, 0x109);
|
||||
CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n",
|
||||
pll_int, pll_frac, pll_post);
|
||||
|
||||
if (pll_post) {
|
||||
int fin, fsc;
|
||||
int pll = 28636363L * ((((u64)pll_int) << 25) + pll_frac);
|
||||
|
||||
pll >>= 25;
|
||||
pll /= pll_post;
|
||||
CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
|
||||
pll / 1000000, pll % 1000000);
|
||||
CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n",
|
||||
pll / 8000000, (pll / 8) % 1000000);
|
||||
|
||||
fin = ((u64)src_decimation * pll) >> 12;
|
||||
CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n",
|
||||
fin / 1000000, fin % 1000000);
|
||||
|
||||
fsc = (((u64)sc) * pll) >> 24L;
|
||||
CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n",
|
||||
fsc / 1000000, fsc % 1000000);
|
||||
|
||||
CX18_DEBUG_INFO("hblank %i, hactive %i, "
|
||||
"vblank %i , vactive %i, vblank656 %i, src_dec %i,"
|
||||
"burst 0x%02x, luma_lpf %i, uv_lpf %i, comb 0x%02x,"
|
||||
" sc 0x%06x\n",
|
||||
hblank, hactive, vblank, vactive, vblank656,
|
||||
src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
|
||||
}
|
||||
|
||||
/* Sets horizontal blanking delay and active lines */
|
||||
cx18_av_write(cx, 0x470, hblank);
|
||||
cx18_av_write(cx, 0x471, 0xff & (((hblank >> 8) & 0x3) |
|
||||
(hactive << 4)));
|
||||
cx18_av_write(cx, 0x472, hactive >> 4);
|
||||
|
||||
/* Sets burst gate delay */
|
||||
cx18_av_write(cx, 0x473, burst);
|
||||
|
||||
/* Sets vertical blanking delay and active duration */
|
||||
cx18_av_write(cx, 0x474, vblank);
|
||||
cx18_av_write(cx, 0x475, 0xff & (((vblank >> 8) & 0x3) |
|
||||
(vactive << 4)));
|
||||
cx18_av_write(cx, 0x476, vactive >> 4);
|
||||
cx18_av_write(cx, 0x477, vblank656);
|
||||
|
||||
/* Sets src decimation rate */
|
||||
cx18_av_write(cx, 0x478, 0xff & src_decimation);
|
||||
cx18_av_write(cx, 0x479, 0xff & (src_decimation >> 8));
|
||||
|
||||
/* Sets Luma and UV Low pass filters */
|
||||
cx18_av_write(cx, 0x47a, luma_lpf << 6 | ((uv_lpf << 4) & 0x30));
|
||||
|
||||
/* Enables comb filters */
|
||||
cx18_av_write(cx, 0x47b, comb);
|
||||
|
||||
/* Sets SC Step*/
|
||||
cx18_av_write(cx, 0x47c, sc);
|
||||
cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
|
||||
cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
|
||||
|
||||
/* Sets VBI parameters */
|
||||
if (std & V4L2_STD_625_50) {
|
||||
cx18_av_write(cx, 0x47f, 0x01);
|
||||
state->vbi_line_offset = 5;
|
||||
} else {
|
||||
cx18_av_write(cx, 0x47f, 0x00);
|
||||
state->vbi_line_offset = 8;
|
||||
}
|
||||
}
|
||||
|
||||
int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct cx18_av_state *state = &cx->av_state;
|
||||
struct v4l2_format *fmt;
|
||||
struct v4l2_sliced_vbi_format *svbi;
|
||||
|
||||
switch (cmd) {
|
||||
case VIDIOC_G_FMT:
|
||||
{
|
||||
static u16 lcr2vbi[] = {
|
||||
0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
|
||||
0, V4L2_SLICED_WSS_625, 0, /* 4 */
|
||||
V4L2_SLICED_CAPTION_525, /* 6 */
|
||||
0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
|
||||
0, 0, 0, 0
|
||||
};
|
||||
int is_pal = !(state->std & V4L2_STD_525_60);
|
||||
int i;
|
||||
|
||||
fmt = arg;
|
||||
if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
|
||||
return -EINVAL;
|
||||
svbi = &fmt->fmt.sliced;
|
||||
memset(svbi, 0, sizeof(*svbi));
|
||||
/* we're done if raw VBI is active */
|
||||
if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
|
||||
break;
|
||||
|
||||
if (is_pal) {
|
||||
for (i = 7; i <= 23; i++) {
|
||||
u8 v = cx18_av_read(cx, 0x424 + i - 7);
|
||||
|
||||
svbi->service_lines[0][i] = lcr2vbi[v >> 4];
|
||||
svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
|
||||
svbi->service_set |= svbi->service_lines[0][i] |
|
||||
svbi->service_lines[1][i];
|
||||
}
|
||||
} else {
|
||||
for (i = 10; i <= 21; i++) {
|
||||
u8 v = cx18_av_read(cx, 0x424 + i - 10);
|
||||
|
||||
svbi->service_lines[0][i] = lcr2vbi[v >> 4];
|
||||
svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
|
||||
svbi->service_set |= svbi->service_lines[0][i] |
|
||||
svbi->service_lines[1][i];
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_S_FMT:
|
||||
{
|
||||
int is_pal = !(state->std & V4L2_STD_525_60);
|
||||
int vbi_offset = is_pal ? 1 : 0;
|
||||
int i, x;
|
||||
u8 lcr[24];
|
||||
|
||||
fmt = arg;
|
||||
if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
|
||||
return -EINVAL;
|
||||
svbi = &fmt->fmt.sliced;
|
||||
if (svbi->service_set == 0) {
|
||||
/* raw VBI */
|
||||
memset(svbi, 0, sizeof(*svbi));
|
||||
|
||||
/* Setup VBI */
|
||||
cx18_av_vbi_setup(cx);
|
||||
|
||||
/* VBI Offset */
|
||||
cx18_av_write(cx, 0x47f, vbi_offset);
|
||||
cx18_av_write(cx, 0x404, 0x2e);
|
||||
break;
|
||||
}
|
||||
|
||||
for (x = 0; x <= 23; x++)
|
||||
lcr[x] = 0x00;
|
||||
|
||||
/* Setup VBI */
|
||||
cx18_av_vbi_setup(cx);
|
||||
|
||||
/* Sliced VBI */
|
||||
cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
|
||||
cx18_av_write(cx, 0x406, 0x13);
|
||||
cx18_av_write(cx, 0x47f, vbi_offset);
|
||||
|
||||
if (is_pal) {
|
||||
for (i = 0; i <= 6; i++)
|
||||
svbi->service_lines[0][i] =
|
||||
svbi->service_lines[1][i] = 0;
|
||||
} else {
|
||||
for (i = 0; i <= 9; i++)
|
||||
svbi->service_lines[0][i] =
|
||||
svbi->service_lines[1][i] = 0;
|
||||
|
||||
for (i = 22; i <= 23; i++)
|
||||
svbi->service_lines[0][i] =
|
||||
svbi->service_lines[1][i] = 0;
|
||||
}
|
||||
|
||||
for (i = 7; i <= 23; i++) {
|
||||
for (x = 0; x <= 1; x++) {
|
||||
switch (svbi->service_lines[1-x][i]) {
|
||||
case V4L2_SLICED_TELETEXT_B:
|
||||
lcr[i] |= 1 << (4 * x);
|
||||
break;
|
||||
case V4L2_SLICED_WSS_625:
|
||||
lcr[i] |= 4 << (4 * x);
|
||||
break;
|
||||
case V4L2_SLICED_CAPTION_525:
|
||||
lcr[i] |= 6 << (4 * x);
|
||||
break;
|
||||
case V4L2_SLICED_VPS:
|
||||
lcr[i] |= 9 << (4 * x);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (is_pal) {
|
||||
for (x = 1, i = 0x424; i <= 0x434; i++, x++)
|
||||
cx18_av_write(cx, i, lcr[6 + x]);
|
||||
} else {
|
||||
for (x = 1, i = 0x424; i <= 0x430; i++, x++)
|
||||
cx18_av_write(cx, i, lcr[9 + x]);
|
||||
for (i = 0x431; i <= 0x434; i++)
|
||||
cx18_av_write(cx, i, 0);
|
||||
}
|
||||
|
||||
cx18_av_write(cx, 0x43c, 0x16);
|
||||
cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_INT_DECODE_VBI_LINE:
|
||||
{
|
||||
struct v4l2_decode_vbi_line *vbi = arg;
|
||||
u8 *p = vbi->p;
|
||||
int id1, id2, l, err = 0;
|
||||
|
||||
if (p[0] || p[1] != 0xff || p[2] != 0xff ||
|
||||
(p[3] != 0x55 && p[3] != 0x91)) {
|
||||
vbi->line = vbi->type = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
p += 4;
|
||||
id1 = p[-1];
|
||||
id2 = p[0] & 0xf;
|
||||
l = p[2] & 0x3f;
|
||||
l += state->vbi_line_offset;
|
||||
p += 4;
|
||||
|
||||
switch (id2) {
|
||||
case 1:
|
||||
id2 = V4L2_SLICED_TELETEXT_B;
|
||||
break;
|
||||
case 4:
|
||||
id2 = V4L2_SLICED_WSS_625;
|
||||
break;
|
||||
case 6:
|
||||
id2 = V4L2_SLICED_CAPTION_525;
|
||||
err = !odd_parity(p[0]) || !odd_parity(p[1]);
|
||||
break;
|
||||
case 9:
|
||||
id2 = V4L2_SLICED_VPS;
|
||||
if (decode_vps(p, p) != 0)
|
||||
err = 1;
|
||||
break;
|
||||
default:
|
||||
id2 = 0;
|
||||
err = 1;
|
||||
break;
|
||||
}
|
||||
|
||||
vbi->type = err ? 0 : id2;
|
||||
vbi->line = err ? 0 : l;
|
||||
vbi->is_second_field = err ? 0 : (id1 == 0x55);
|
||||
vbi->p = p;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
/*
|
||||
* cx18 functions to query card hardware
|
||||
*
|
||||
* Derived from ivtv-cards.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-i2c.h"
|
||||
#include <media/cs5345.h>
|
||||
|
||||
/********************** card configuration *******************************/
|
||||
|
||||
/* usual i2c tuner addresses to probe */
|
||||
static struct cx18_card_tuner_i2c cx18_i2c_std = {
|
||||
.radio = { I2C_CLIENT_END },
|
||||
.demod = { 0x43, I2C_CLIENT_END },
|
||||
.tv = { 0x61, 0x60, I2C_CLIENT_END },
|
||||
};
|
||||
|
||||
/* Please add new PCI IDs to: http://pci-ids.ucw.cz/iii
|
||||
This keeps the PCI ID database up to date. Note that the entries
|
||||
must be added under vendor 0x4444 (Conexant) as subsystem IDs.
|
||||
New vendor IDs should still be added to the vendor ID list. */
|
||||
|
||||
/* Hauppauge HVR-1600 cards */
|
||||
|
||||
/* Note: for Hauppauge cards the tveeprom information is used instead
|
||||
of PCI IDs */
|
||||
static const struct cx18_card cx18_card_hvr1600_esmt = {
|
||||
.type = CX18_CARD_HVR_1600_ESMT,
|
||||
.name = "Hauppauge HVR-1600",
|
||||
.comment = "DVB & VBI are not yet supported\n",
|
||||
.v4l2_capabilities = CX18_CAP_ENCODER,
|
||||
.hw_audio_ctrl = CX18_HW_CX23418,
|
||||
.hw_muxer = CX18_HW_CS5345,
|
||||
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
|
||||
.video_inputs = {
|
||||
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
|
||||
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
|
||||
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
|
||||
{ CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
|
||||
{ CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
|
||||
},
|
||||
.audio_inputs = {
|
||||
{ CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
|
||||
{ CX18_CARD_INPUT_LINE_IN1,
|
||||
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
|
||||
{ CX18_CARD_INPUT_LINE_IN2,
|
||||
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
|
||||
},
|
||||
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO_SERIAL, 0 },
|
||||
.ddr = {
|
||||
/* ESMT M13S128324A-5B memory */
|
||||
.chip_config = 0x003,
|
||||
.refresh = 0x30c,
|
||||
.timing1 = 0x44220e82,
|
||||
.timing2 = 0x08,
|
||||
.tune_lane = 0,
|
||||
.initial_emrs = 0,
|
||||
},
|
||||
.gpio_init.initial_value = 0x3001,
|
||||
.gpio_init.direction = 0x3001,
|
||||
.i2c = &cx18_i2c_std,
|
||||
};
|
||||
|
||||
static const struct cx18_card cx18_card_hvr1600_samsung = {
|
||||
.type = CX18_CARD_HVR_1600_SAMSUNG,
|
||||
.name = "Hauppauge HVR-1600 (Preproduction)",
|
||||
.comment = "DVB & VBI are not yet supported\n",
|
||||
.v4l2_capabilities = CX18_CAP_ENCODER,
|
||||
.hw_audio_ctrl = CX18_HW_CX23418,
|
||||
.hw_muxer = CX18_HW_CS5345,
|
||||
.hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER | CX18_HW_CS5345,
|
||||
.video_inputs = {
|
||||
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
|
||||
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
|
||||
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
|
||||
{ CX18_CARD_INPUT_SVIDEO2, 2, CX23418_SVIDEO2 },
|
||||
{ CX18_CARD_INPUT_COMPOSITE2, 2, CX23418_COMPOSITE4 },
|
||||
},
|
||||
.audio_inputs = {
|
||||
{ CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO8, CS5345_IN_1 | CS5345_MCLK_1_5 },
|
||||
{ CX18_CARD_INPUT_LINE_IN1,
|
||||
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
|
||||
{ CX18_CARD_INPUT_LINE_IN2,
|
||||
CX23418_AUDIO_SERIAL, CS5345_IN_2 },
|
||||
},
|
||||
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO_SERIAL, 0 },
|
||||
.ddr = {
|
||||
/* Samsung K4D263238G-VC33 memory */
|
||||
.chip_config = 0x003,
|
||||
.refresh = 0x30c,
|
||||
.timing1 = 0x23230b73,
|
||||
.timing2 = 0x08,
|
||||
.tune_lane = 0,
|
||||
.initial_emrs = 2,
|
||||
},
|
||||
.gpio_init.initial_value = 0x3001,
|
||||
.gpio_init.direction = 0x3001,
|
||||
.i2c = &cx18_i2c_std,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Compro VideoMate H900: not working at the moment! */
|
||||
|
||||
static const struct cx18_card_pci_info cx18_pci_h900[] = {
|
||||
{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_COMPRO, 0xe100 },
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
static const struct cx18_card cx18_card_h900 = {
|
||||
.type = CX18_CARD_COMPRO_H900,
|
||||
.name = "Compro VideoMate H900",
|
||||
.comment = "Not yet supported!\n",
|
||||
.v4l2_capabilities = 0,
|
||||
.hw_audio_ctrl = CX18_HW_CX23418,
|
||||
.hw_all = CX18_HW_TUNER,
|
||||
.video_inputs = {
|
||||
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
|
||||
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
|
||||
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
|
||||
},
|
||||
.audio_inputs = {
|
||||
{ CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO8, 0 },
|
||||
{ CX18_CARD_INPUT_LINE_IN1,
|
||||
CX23418_AUDIO_SERIAL, 0 },
|
||||
},
|
||||
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO_SERIAL, 0 },
|
||||
.tuners = {
|
||||
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
|
||||
},
|
||||
.ddr = {
|
||||
/* EtronTech EM6A9160TS-5G memory */
|
||||
.chip_config = 0x50003,
|
||||
.refresh = 0x753,
|
||||
.timing1 = 0x24330e84,
|
||||
.timing2 = 0x1f,
|
||||
.tune_lane = 0,
|
||||
.initial_emrs = 0,
|
||||
},
|
||||
.pci_list = cx18_pci_h900,
|
||||
.i2c = &cx18_i2c_std,
|
||||
};
|
||||
|
||||
/* ------------------------------------------------------------------------- */
|
||||
|
||||
/* Yuan MPC718: not working at the moment! */
|
||||
|
||||
static const struct cx18_card_pci_info cx18_pci_mpc718[] = {
|
||||
{ PCI_DEVICE_ID_CX23418, CX18_PCI_ID_YUAN, 0x0718 },
|
||||
{ 0, 0, 0 }
|
||||
};
|
||||
|
||||
static const struct cx18_card cx18_card_mpc718 = {
|
||||
.type = CX18_CARD_YUAN_MPC718,
|
||||
.name = "Yuan MPC718",
|
||||
.comment = "Not yet supported!\n",
|
||||
.v4l2_capabilities = 0,
|
||||
.hw_audio_ctrl = CX18_HW_CX23418,
|
||||
.hw_all = CX18_HW_TUNER,
|
||||
.video_inputs = {
|
||||
{ CX18_CARD_INPUT_VID_TUNER, 0, CX23418_COMPOSITE7 },
|
||||
{ CX18_CARD_INPUT_SVIDEO1, 1, CX23418_SVIDEO1 },
|
||||
{ CX18_CARD_INPUT_COMPOSITE1, 1, CX23418_COMPOSITE3 },
|
||||
},
|
||||
.audio_inputs = {
|
||||
{ CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO8, 0 },
|
||||
{ CX18_CARD_INPUT_LINE_IN1,
|
||||
CX23418_AUDIO_SERIAL, 0 },
|
||||
},
|
||||
.radio_input = { CX18_CARD_INPUT_AUD_TUNER,
|
||||
CX23418_AUDIO_SERIAL, 0 },
|
||||
.tuners = {
|
||||
/* XC3028 tuner */
|
||||
{ .std = V4L2_STD_ALL, .tuner = TUNER_XC2028 },
|
||||
},
|
||||
/* tuner reset */
|
||||
.gpio_init = { .direction = 0x1000, .initial_value = 0x1000 },
|
||||
.ddr = {
|
||||
/* Probably Samsung K4D263238G-VC33 memory */
|
||||
.chip_config = 0x003,
|
||||
.refresh = 0x30c,
|
||||
.timing1 = 0x23230b73,
|
||||
.timing2 = 0x08,
|
||||
.tune_lane = 0,
|
||||
.initial_emrs = 2,
|
||||
},
|
||||
.pci_list = cx18_pci_mpc718,
|
||||
.i2c = &cx18_i2c_std,
|
||||
};
|
||||
|
||||
static const struct cx18_card *cx18_card_list[] = {
|
||||
&cx18_card_hvr1600_esmt,
|
||||
&cx18_card_hvr1600_samsung,
|
||||
&cx18_card_h900,
|
||||
&cx18_card_mpc718,
|
||||
};
|
||||
|
||||
const struct cx18_card *cx18_get_card(u16 index)
|
||||
{
|
||||
if (index >= ARRAY_SIZE(cx18_card_list))
|
||||
return NULL;
|
||||
return cx18_card_list[index];
|
||||
}
|
||||
|
||||
int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input)
|
||||
{
|
||||
const struct cx18_card_video_input *card_input =
|
||||
cx->card->video_inputs + index;
|
||||
static const char * const input_strs[] = {
|
||||
"Tuner 1",
|
||||
"S-Video 1",
|
||||
"S-Video 2",
|
||||
"Composite 1",
|
||||
"Composite 2",
|
||||
"Composite 3"
|
||||
};
|
||||
|
||||
memset(input, 0, sizeof(*input));
|
||||
if (index >= cx->nof_inputs)
|
||||
return -EINVAL;
|
||||
input->index = index;
|
||||
strlcpy(input->name, input_strs[card_input->video_type - 1],
|
||||
sizeof(input->name));
|
||||
input->type = (card_input->video_type == CX18_CARD_INPUT_VID_TUNER ?
|
||||
V4L2_INPUT_TYPE_TUNER : V4L2_INPUT_TYPE_CAMERA);
|
||||
input->audioset = (1 << cx->nof_audio_inputs) - 1;
|
||||
input->std = (input->type == V4L2_INPUT_TYPE_TUNER) ?
|
||||
cx->tuner_std : V4L2_STD_ALL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *audio)
|
||||
{
|
||||
const struct cx18_card_audio_input *aud_input =
|
||||
cx->card->audio_inputs + index;
|
||||
static const char * const input_strs[] = {
|
||||
"Tuner 1",
|
||||
"Line In 1",
|
||||
"Line In 2"
|
||||
};
|
||||
|
||||
memset(audio, 0, sizeof(*audio));
|
||||
if (index >= cx->nof_audio_inputs)
|
||||
return -EINVAL;
|
||||
strlcpy(audio->name, input_strs[aud_input->audio_type - 1],
|
||||
sizeof(audio->name));
|
||||
audio->index = index;
|
||||
audio->capability = V4L2_AUDCAP_STEREO;
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* cx18 functions to query card hardware
|
||||
*
|
||||
* Derived from ivtv-cards.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* hardware flags */
|
||||
#define CX18_HW_TUNER (1 << 0)
|
||||
#define CX18_HW_TVEEPROM (1 << 1)
|
||||
#define CX18_HW_CS5345 (1 << 2)
|
||||
#define CX18_HW_GPIO (1 << 3)
|
||||
#define CX18_HW_CX23418 (1 << 4)
|
||||
#define CX18_HW_DVB (1 << 5)
|
||||
|
||||
/* video inputs */
|
||||
#define CX18_CARD_INPUT_VID_TUNER 1
|
||||
#define CX18_CARD_INPUT_SVIDEO1 2
|
||||
#define CX18_CARD_INPUT_SVIDEO2 3
|
||||
#define CX18_CARD_INPUT_COMPOSITE1 4
|
||||
#define CX18_CARD_INPUT_COMPOSITE2 5
|
||||
#define CX18_CARD_INPUT_COMPOSITE3 6
|
||||
|
||||
enum cx34180_video_input {
|
||||
/* Composite video inputs In1-In8 */
|
||||
CX23418_COMPOSITE1 = 1,
|
||||
CX23418_COMPOSITE2,
|
||||
CX23418_COMPOSITE3,
|
||||
CX23418_COMPOSITE4,
|
||||
CX23418_COMPOSITE5,
|
||||
CX23418_COMPOSITE6,
|
||||
CX23418_COMPOSITE7,
|
||||
CX23418_COMPOSITE8,
|
||||
|
||||
/* S-Video inputs consist of one luma input (In1-In4) ORed with one
|
||||
chroma input (In5-In8) */
|
||||
CX23418_SVIDEO_LUMA1 = 0x10,
|
||||
CX23418_SVIDEO_LUMA2 = 0x20,
|
||||
CX23418_SVIDEO_LUMA3 = 0x30,
|
||||
CX23418_SVIDEO_LUMA4 = 0x40,
|
||||
CX23418_SVIDEO_CHROMA4 = 0x400,
|
||||
CX23418_SVIDEO_CHROMA5 = 0x500,
|
||||
CX23418_SVIDEO_CHROMA6 = 0x600,
|
||||
CX23418_SVIDEO_CHROMA7 = 0x700,
|
||||
CX23418_SVIDEO_CHROMA8 = 0x800,
|
||||
|
||||
/* S-Video aliases for common luma/chroma combinations */
|
||||
CX23418_SVIDEO1 = 0x510,
|
||||
CX23418_SVIDEO2 = 0x620,
|
||||
CX23418_SVIDEO3 = 0x730,
|
||||
CX23418_SVIDEO4 = 0x840,
|
||||
};
|
||||
|
||||
/* audio inputs */
|
||||
#define CX18_CARD_INPUT_AUD_TUNER 1
|
||||
#define CX18_CARD_INPUT_LINE_IN1 2
|
||||
#define CX18_CARD_INPUT_LINE_IN2 3
|
||||
|
||||
#define CX18_CARD_MAX_VIDEO_INPUTS 6
|
||||
#define CX18_CARD_MAX_AUDIO_INPUTS 3
|
||||
#define CX18_CARD_MAX_TUNERS 2
|
||||
|
||||
enum cx23418_audio_input {
|
||||
/* Audio inputs: serial or In4-In8 */
|
||||
CX23418_AUDIO_SERIAL,
|
||||
CX23418_AUDIO4 = 4,
|
||||
CX23418_AUDIO5,
|
||||
CX23418_AUDIO6,
|
||||
CX23418_AUDIO7,
|
||||
CX23418_AUDIO8,
|
||||
};
|
||||
|
||||
/* V4L2 capability aliases */
|
||||
#define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
|
||||
V4L2_CAP_AUDIO | V4L2_CAP_READWRITE)
|
||||
/* | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
|
||||
|
||||
struct cx18_card_video_input {
|
||||
u8 video_type; /* video input type */
|
||||
u8 audio_index; /* index in cx18_card_audio_input array */
|
||||
u16 video_input; /* hardware video input */
|
||||
};
|
||||
|
||||
struct cx18_card_audio_input {
|
||||
u8 audio_type; /* audio input type */
|
||||
u32 audio_input; /* hardware audio input */
|
||||
u16 muxer_input; /* hardware muxer input for boards with a
|
||||
multiplexer chip */
|
||||
};
|
||||
|
||||
struct cx18_card_pci_info {
|
||||
u16 device;
|
||||
u16 subsystem_vendor;
|
||||
u16 subsystem_device;
|
||||
};
|
||||
|
||||
/* GPIO definitions */
|
||||
|
||||
/* The mask is the set of bits used by the operation */
|
||||
|
||||
struct cx18_gpio_init { /* set initial GPIO DIR and OUT values */
|
||||
u16 direction; /* DIR setting. Leave to 0 if no init is needed */
|
||||
u16 initial_value;
|
||||
};
|
||||
|
||||
struct cx18_card_tuner {
|
||||
v4l2_std_id std; /* standard for which the tuner is suitable */
|
||||
int tuner; /* tuner ID (from tuner.h) */
|
||||
};
|
||||
|
||||
struct cx18_card_tuner_i2c {
|
||||
unsigned short radio[2];/* radio tuner i2c address to probe */
|
||||
unsigned short demod[2];/* demodulator i2c address to probe */
|
||||
unsigned short tv[4]; /* tv tuner i2c addresses to probe */
|
||||
};
|
||||
|
||||
struct cx18_ddr { /* DDR config data */
|
||||
u32 chip_config;
|
||||
u32 refresh;
|
||||
u32 timing1;
|
||||
u32 timing2;
|
||||
u32 tune_lane;
|
||||
u32 initial_emrs;
|
||||
};
|
||||
|
||||
/* for card information/parameters */
|
||||
struct cx18_card {
|
||||
int type;
|
||||
char *name;
|
||||
char *comment;
|
||||
u32 v4l2_capabilities;
|
||||
u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only
|
||||
1 dev allowed) */
|
||||
u32 hw_muxer; /* hardware used to multiplex audio input */
|
||||
u32 hw_all; /* all hardware used by the board */
|
||||
struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
|
||||
struct cx18_card_audio_input audio_inputs[CX18_CARD_MAX_AUDIO_INPUTS];
|
||||
struct cx18_card_audio_input radio_input;
|
||||
|
||||
/* GPIO card-specific settings */
|
||||
struct cx18_gpio_init gpio_init;
|
||||
|
||||
struct cx18_card_tuner tuners[CX18_CARD_MAX_TUNERS];
|
||||
struct cx18_card_tuner_i2c *i2c;
|
||||
|
||||
struct cx18_ddr ddr;
|
||||
|
||||
/* list of device and subsystem vendor/devices that
|
||||
correspond to this card type. */
|
||||
const struct cx18_card_pci_info *pci_list;
|
||||
};
|
||||
|
||||
int cx18_get_input(struct cx18 *cx, u16 index, struct v4l2_input *input);
|
||||
int cx18_get_audio_input(struct cx18 *cx, u16 index, struct v4l2_audio *input);
|
||||
const struct cx18_card *cx18_get_card(u16 index);
|
|
@ -0,0 +1,306 @@
|
|||
/*
|
||||
* cx18 ioctl control functions
|
||||
*
|
||||
* Derived from ivtv-controls.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-av-core.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-ioctl.h"
|
||||
#include "cx18-audio.h"
|
||||
#include "cx18-i2c.h"
|
||||
#include "cx18-mailbox.h"
|
||||
#include "cx18-controls.h"
|
||||
|
||||
static const u32 user_ctrls[] = {
|
||||
V4L2_CID_USER_CLASS,
|
||||
V4L2_CID_BRIGHTNESS,
|
||||
V4L2_CID_CONTRAST,
|
||||
V4L2_CID_SATURATION,
|
||||
V4L2_CID_HUE,
|
||||
V4L2_CID_AUDIO_VOLUME,
|
||||
V4L2_CID_AUDIO_BALANCE,
|
||||
V4L2_CID_AUDIO_BASS,
|
||||
V4L2_CID_AUDIO_TREBLE,
|
||||
V4L2_CID_AUDIO_MUTE,
|
||||
V4L2_CID_AUDIO_LOUDNESS,
|
||||
0
|
||||
};
|
||||
|
||||
static const u32 *ctrl_classes[] = {
|
||||
user_ctrls,
|
||||
cx2341x_mpeg_ctrls,
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cx18_queryctrl(struct cx18 *cx, struct v4l2_queryctrl *qctrl)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
CX18_DEBUG_IOCTL("VIDIOC_QUERYCTRL(%08x)\n", qctrl->id);
|
||||
|
||||
qctrl->id = v4l2_ctrl_next(ctrl_classes, qctrl->id);
|
||||
if (qctrl->id == 0)
|
||||
return -EINVAL;
|
||||
|
||||
switch (qctrl->id) {
|
||||
/* Standard V4L2 controls */
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
case V4L2_CID_HUE:
|
||||
case V4L2_CID_SATURATION:
|
||||
case V4L2_CID_CONTRAST:
|
||||
if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl))
|
||||
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
|
||||
return 0;
|
||||
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
case V4L2_CID_AUDIO_LOUDNESS:
|
||||
if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
|
||||
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
|
||||
return 0;
|
||||
|
||||
default:
|
||||
if (cx2341x_ctrl_query(&cx->params, qctrl))
|
||||
qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
|
||||
return 0;
|
||||
}
|
||||
strncpy(qctrl->name, name, sizeof(qctrl->name) - 1);
|
||||
qctrl->name[sizeof(qctrl->name) - 1] = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_querymenu(struct cx18 *cx, struct v4l2_querymenu *qmenu)
|
||||
{
|
||||
struct v4l2_queryctrl qctrl;
|
||||
|
||||
qctrl.id = qmenu->id;
|
||||
cx18_queryctrl(cx, &qctrl);
|
||||
return v4l2_ctrl_query_menu(qmenu, &qctrl, cx2341x_ctrl_get_menu(qmenu->id));
|
||||
}
|
||||
|
||||
static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
|
||||
{
|
||||
s32 v = vctrl->value;
|
||||
|
||||
CX18_DEBUG_IOCTL("VIDIOC_S_CTRL(%08x, %x)\n", vctrl->id, v);
|
||||
|
||||
switch (vctrl->id) {
|
||||
/* Standard V4L2 controls */
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
case V4L2_CID_HUE:
|
||||
case V4L2_CID_SATURATION:
|
||||
case V4L2_CID_CONTRAST:
|
||||
return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl);
|
||||
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
case V4L2_CID_AUDIO_LOUDNESS:
|
||||
return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
|
||||
|
||||
default:
|
||||
CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
|
||||
{
|
||||
CX18_DEBUG_IOCTL("VIDIOC_G_CTRL(%08x)\n", vctrl->id);
|
||||
|
||||
switch (vctrl->id) {
|
||||
/* Standard V4L2 controls */
|
||||
case V4L2_CID_BRIGHTNESS:
|
||||
case V4L2_CID_HUE:
|
||||
case V4L2_CID_SATURATION:
|
||||
case V4L2_CID_CONTRAST:
|
||||
return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl);
|
||||
|
||||
case V4L2_CID_AUDIO_VOLUME:
|
||||
case V4L2_CID_AUDIO_MUTE:
|
||||
case V4L2_CID_AUDIO_BALANCE:
|
||||
case V4L2_CID_AUDIO_BASS:
|
||||
case V4L2_CID_AUDIO_TREBLE:
|
||||
case V4L2_CID_AUDIO_LOUDNESS:
|
||||
return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
|
||||
default:
|
||||
CX18_DEBUG_IOCTL("invalid control %x\n", vctrl->id);
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt)
|
||||
{
|
||||
if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
|
||||
return -EINVAL;
|
||||
if (atomic_read(&cx->capturing) > 0)
|
||||
return -EBUSY;
|
||||
|
||||
/* First try to allocate sliced VBI buffers if needed. */
|
||||
if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CX18_VBI_FRAMES; i++) {
|
||||
/* Yuck, hardcoded. Needs to be a define */
|
||||
cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
|
||||
if (cx->vbi.sliced_mpeg_data[i] == NULL) {
|
||||
while (--i >= 0) {
|
||||
kfree(cx->vbi.sliced_mpeg_data[i]);
|
||||
cx->vbi.sliced_mpeg_data[i] = NULL;
|
||||
}
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cx->vbi.insert_mpeg = fmt;
|
||||
|
||||
if (cx->vbi.insert_mpeg == 0)
|
||||
return 0;
|
||||
/* Need sliced data for mpeg insertion */
|
||||
if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
|
||||
if (cx->is_60hz)
|
||||
cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
|
||||
else
|
||||
cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
|
||||
cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct v4l2_control ctrl;
|
||||
|
||||
switch (cmd) {
|
||||
case VIDIOC_QUERYMENU:
|
||||
CX18_DEBUG_IOCTL("VIDIOC_QUERYMENU\n");
|
||||
return cx18_querymenu(cx, arg);
|
||||
|
||||
case VIDIOC_QUERYCTRL:
|
||||
return cx18_queryctrl(cx, arg);
|
||||
|
||||
case VIDIOC_S_CTRL:
|
||||
return cx18_s_ctrl(cx, arg);
|
||||
|
||||
case VIDIOC_G_CTRL:
|
||||
return cx18_g_ctrl(cx, arg);
|
||||
|
||||
case VIDIOC_S_EXT_CTRLS:
|
||||
{
|
||||
struct v4l2_ext_controls *c = arg;
|
||||
|
||||
if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
|
||||
int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < c->count; i++) {
|
||||
ctrl.id = c->controls[i].id;
|
||||
ctrl.value = c->controls[i].value;
|
||||
err = cx18_s_ctrl(cx, &ctrl);
|
||||
c->controls[i].value = ctrl.value;
|
||||
if (err) {
|
||||
c->error_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
CX18_DEBUG_IOCTL("VIDIOC_S_EXT_CTRLS\n");
|
||||
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
|
||||
struct cx2341x_mpeg_params p = cx->params;
|
||||
int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->capturing), arg, cmd);
|
||||
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (p.video_encoding != cx->params.video_encoding) {
|
||||
int is_mpeg1 = p.video_encoding ==
|
||||
V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
|
||||
struct v4l2_format fmt;
|
||||
|
||||
/* fix videodecoder resolution */
|
||||
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
||||
fmt.fmt.pix.width = cx->params.width / (is_mpeg1 ? 2 : 1);
|
||||
fmt.fmt.pix.height = cx->params.height;
|
||||
cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
|
||||
}
|
||||
err = cx2341x_update(cx, cx18_api_func, &cx->params, &p);
|
||||
if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
|
||||
err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
|
||||
cx->params = p;
|
||||
cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
|
||||
cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
|
||||
return err;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case VIDIOC_G_EXT_CTRLS:
|
||||
{
|
||||
struct v4l2_ext_controls *c = arg;
|
||||
|
||||
if (c->ctrl_class == V4L2_CTRL_CLASS_USER) {
|
||||
int i;
|
||||
int err = 0;
|
||||
|
||||
for (i = 0; i < c->count; i++) {
|
||||
ctrl.id = c->controls[i].id;
|
||||
ctrl.value = c->controls[i].value;
|
||||
err = cx18_g_ctrl(cx, &ctrl);
|
||||
c->controls[i].value = ctrl.value;
|
||||
if (err) {
|
||||
c->error_idx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
CX18_DEBUG_IOCTL("VIDIOC_G_EXT_CTRLS\n");
|
||||
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
|
||||
return cx2341x_ext_ctrls(&cx->params, 0, arg, cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case VIDIOC_TRY_EXT_CTRLS:
|
||||
{
|
||||
struct v4l2_ext_controls *c = arg;
|
||||
|
||||
CX18_DEBUG_IOCTL("VIDIOC_TRY_EXT_CTRLS\n");
|
||||
if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG)
|
||||
return cx2341x_ext_ctrls(&cx->params,
|
||||
atomic_read(&cx->capturing), arg, cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* cx18 ioctl control functions
|
||||
*
|
||||
* Derived from ivtv-controls.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
int cx18_control_ioctls(struct cx18 *cx, unsigned int cmd, void *arg);
|
|
@ -0,0 +1,971 @@
|
|||
/*
|
||||
* cx18 driver initialization and card probing
|
||||
*
|
||||
* Derived from ivtv-driver.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-version.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-i2c.h"
|
||||
#include "cx18-irq.h"
|
||||
#include "cx18-gpio.h"
|
||||
#include "cx18-firmware.h"
|
||||
#include "cx18-streams.h"
|
||||
#include "cx18-av-core.h"
|
||||
#include "cx18-scb.h"
|
||||
#include "cx18-mailbox.h"
|
||||
#include "cx18-ioctl.h"
|
||||
#include "tuner-xc2028.h"
|
||||
|
||||
#include <media/tveeprom.h>
|
||||
|
||||
|
||||
/* var to keep track of the number of array elements in use */
|
||||
int cx18_cards_active;
|
||||
|
||||
/* If you have already X v4l cards, then set this to X. This way
|
||||
the device numbers stay matched. Example: you have a WinTV card
|
||||
without radio and a Compro H900 with. Normally this would give a
|
||||
video1 device together with a radio0 device for the Compro. By
|
||||
setting this to 1 you ensure that radio0 is now also radio1. */
|
||||
int cx18_first_minor;
|
||||
|
||||
/* Master variable for all cx18 info */
|
||||
struct cx18 *cx18_cards[CX18_MAX_CARDS];
|
||||
|
||||
/* Protects cx18_cards_active */
|
||||
DEFINE_SPINLOCK(cx18_cards_lock);
|
||||
|
||||
/* add your revision and whatnot here */
|
||||
static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
|
||||
{PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
|
||||
PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
|
||||
{0,}
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
|
||||
|
||||
/* Parameter declarations */
|
||||
static int cardtype[CX18_MAX_CARDS];
|
||||
static int tuner[CX18_MAX_CARDS] = { -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 };
|
||||
static int radio[CX18_MAX_CARDS] = { -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 };
|
||||
|
||||
static int cardtype_c = 1;
|
||||
static int tuner_c = 1;
|
||||
static int radio_c = 1;
|
||||
static char pal[] = "--";
|
||||
static char secam[] = "--";
|
||||
static char ntsc[] = "-";
|
||||
|
||||
/* Buffers */
|
||||
static int enc_mpg_buffers = CX18_DEFAULT_ENC_MPG_BUFFERS;
|
||||
static int enc_ts_buffers = CX18_DEFAULT_ENC_TS_BUFFERS;
|
||||
static int enc_yuv_buffers = CX18_DEFAULT_ENC_YUV_BUFFERS;
|
||||
static int enc_vbi_buffers = CX18_DEFAULT_ENC_VBI_BUFFERS;
|
||||
static int enc_pcm_buffers = CX18_DEFAULT_ENC_PCM_BUFFERS;
|
||||
|
||||
static int cx18_pci_latency = 1;
|
||||
|
||||
int cx18_debug;
|
||||
|
||||
module_param_array(tuner, int, &tuner_c, 0644);
|
||||
module_param_array(radio, bool, &radio_c, 0644);
|
||||
module_param_array(cardtype, int, &cardtype_c, 0644);
|
||||
module_param_string(pal, pal, sizeof(pal), 0644);
|
||||
module_param_string(secam, secam, sizeof(secam), 0644);
|
||||
module_param_string(ntsc, ntsc, sizeof(ntsc), 0644);
|
||||
module_param_named(debug, cx18_debug, int, 0644);
|
||||
module_param(cx18_pci_latency, int, 0644);
|
||||
module_param(cx18_first_minor, int, 0644);
|
||||
|
||||
module_param(enc_mpg_buffers, int, 0644);
|
||||
module_param(enc_ts_buffers, int, 0644);
|
||||
module_param(enc_yuv_buffers, int, 0644);
|
||||
module_param(enc_vbi_buffers, int, 0644);
|
||||
module_param(enc_pcm_buffers, int, 0644);
|
||||
|
||||
MODULE_PARM_DESC(tuner, "Tuner type selection,\n"
|
||||
"\t\t\tsee tuner.h for values");
|
||||
MODULE_PARM_DESC(radio,
|
||||
"Enable or disable the radio. Use only if autodetection\n"
|
||||
"\t\t\tfails. 0 = disable, 1 = enable");
|
||||
MODULE_PARM_DESC(cardtype,
|
||||
"Only use this option if your card is not detected properly.\n"
|
||||
"\t\tSpecify card type:\n"
|
||||
"\t\t\t 1 = Hauppauge HVR 1600 (ESMT memory)\n"
|
||||
"\t\t\t 2 = Hauppauge HVR 1600 (Samsung memory)\n"
|
||||
"\t\t\t 3 = Compro VideoMate H900\n"
|
||||
"\t\t\t 4 = Yuan MPC718\n"
|
||||
"\t\t\t 0 = Autodetect (default)\n"
|
||||
"\t\t\t-1 = Ignore this card\n\t\t");
|
||||
MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
|
||||
MODULE_PARM_DESC(secam, "Set SECAM standard: B, G, H, D, K, L, LC");
|
||||
MODULE_PARM_DESC(ntsc, "Set NTSC standard: M, J, K");
|
||||
MODULE_PARM_DESC(debug,
|
||||
"Debug level (bitmask). Default: 0\n"
|
||||
"\t\t\t 1/0x0001: warning\n"
|
||||
"\t\t\t 2/0x0002: info\n"
|
||||
"\t\t\t 4/0x0004: mailbox\n"
|
||||
"\t\t\t 8/0x0008: dma\n"
|
||||
"\t\t\t 16/0x0010: ioctl\n"
|
||||
"\t\t\t 32/0x0020: file\n"
|
||||
"\t\t\t 64/0x0040: i2c\n"
|
||||
"\t\t\t128/0x0080: irq\n"
|
||||
"\t\t\t256/0x0100: high volume\n");
|
||||
MODULE_PARM_DESC(cx18_pci_latency,
|
||||
"Change the PCI latency to 64 if lower: 0 = No, 1 = Yes,\n"
|
||||
"\t\t\tDefault: Yes");
|
||||
MODULE_PARM_DESC(enc_mpg_buffers,
|
||||
"Encoder MPG Buffers (in MB)\n"
|
||||
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_MPG_BUFFERS));
|
||||
MODULE_PARM_DESC(enc_ts_buffers,
|
||||
"Encoder TS Buffers (in MB)\n"
|
||||
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_TS_BUFFERS));
|
||||
MODULE_PARM_DESC(enc_yuv_buffers,
|
||||
"Encoder YUV Buffers (in MB)\n"
|
||||
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_YUV_BUFFERS));
|
||||
MODULE_PARM_DESC(enc_vbi_buffers,
|
||||
"Encoder VBI Buffers (in MB)\n"
|
||||
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_VBI_BUFFERS));
|
||||
MODULE_PARM_DESC(enc_pcm_buffers,
|
||||
"Encoder PCM buffers (in MB)\n"
|
||||
"\t\t\tDefault: " __stringify(CX18_DEFAULT_ENC_PCM_BUFFERS));
|
||||
|
||||
MODULE_PARM_DESC(cx18_first_minor, "Set minor assigned to first card");
|
||||
|
||||
MODULE_AUTHOR("Hans Verkuil");
|
||||
MODULE_DESCRIPTION("CX23418 driver");
|
||||
MODULE_SUPPORTED_DEVICE("CX23418 MPEG2 encoder");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
MODULE_VERSION(CX18_VERSION);
|
||||
|
||||
int cx18_waitq(wait_queue_head_t *waitq)
|
||||
{
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
prepare_to_wait(waitq, &wait, TASK_INTERRUPTIBLE);
|
||||
schedule();
|
||||
finish_wait(waitq, &wait);
|
||||
return signal_pending(current) ? -EINTR : 0;
|
||||
}
|
||||
|
||||
/* Generic utility functions */
|
||||
int cx18_msleep_timeout(unsigned int msecs, int intr)
|
||||
{
|
||||
int timeout = msecs_to_jiffies(msecs);
|
||||
int sig;
|
||||
|
||||
do {
|
||||
set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
|
||||
timeout = schedule_timeout(timeout);
|
||||
sig = intr ? signal_pending(current) : 0;
|
||||
} while (!sig && timeout);
|
||||
return sig;
|
||||
}
|
||||
|
||||
/* Release ioremapped memory */
|
||||
static void cx18_iounmap(struct cx18 *cx)
|
||||
{
|
||||
if (cx == NULL)
|
||||
return;
|
||||
|
||||
/* Release io memory */
|
||||
if (cx->enc_mem != NULL) {
|
||||
CX18_DEBUG_INFO("releasing enc_mem\n");
|
||||
iounmap(cx->enc_mem);
|
||||
cx->enc_mem = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hauppauge card? get values from tveeprom */
|
||||
void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
|
||||
{
|
||||
u8 eedata[256];
|
||||
|
||||
cx->i2c_client[0].addr = 0xA0 >> 1;
|
||||
tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata));
|
||||
tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata);
|
||||
}
|
||||
|
||||
static void cx18_process_eeprom(struct cx18 *cx)
|
||||
{
|
||||
struct tveeprom tv;
|
||||
|
||||
cx18_read_eeprom(cx, &tv);
|
||||
|
||||
/* Many thanks to Steven Toth from Hauppauge for providing the
|
||||
model numbers */
|
||||
switch (tv.model) {
|
||||
case 74000 ... 74099:
|
||||
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
|
||||
break;
|
||||
case 74700 ... 74799:
|
||||
cx->card = cx18_get_card(CX18_CARD_HVR_1600_SAMSUNG);
|
||||
break;
|
||||
case 0:
|
||||
CX18_ERR("Invalid EEPROM\n");
|
||||
return;
|
||||
default:
|
||||
CX18_ERR("Unknown model %d, defaulting to HVR-1600\n", tv.model);
|
||||
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
|
||||
break;
|
||||
}
|
||||
|
||||
cx->v4l2_cap = cx->card->v4l2_capabilities;
|
||||
cx->card_name = cx->card->name;
|
||||
cx->card_i2c = cx->card->i2c;
|
||||
|
||||
CX18_INFO("Autodetected %s\n", cx->card_name);
|
||||
|
||||
if (tv.tuner_type == TUNER_ABSENT)
|
||||
CX18_ERR("tveeprom cannot autodetect tuner!");
|
||||
|
||||
if (cx->options.tuner == -1)
|
||||
cx->options.tuner = tv.tuner_type;
|
||||
if (cx->options.radio == -1)
|
||||
cx->options.radio = (tv.has_radio != 0);
|
||||
|
||||
if (cx->std != 0)
|
||||
/* user specified tuner standard */
|
||||
return;
|
||||
|
||||
/* autodetect tuner standard */
|
||||
if (tv.tuner_formats & V4L2_STD_PAL) {
|
||||
CX18_DEBUG_INFO("PAL tuner detected\n");
|
||||
cx->std |= V4L2_STD_PAL_BG | V4L2_STD_PAL_H;
|
||||
} else if (tv.tuner_formats & V4L2_STD_NTSC) {
|
||||
CX18_DEBUG_INFO("NTSC tuner detected\n");
|
||||
cx->std |= V4L2_STD_NTSC_M;
|
||||
} else if (tv.tuner_formats & V4L2_STD_SECAM) {
|
||||
CX18_DEBUG_INFO("SECAM tuner detected\n");
|
||||
cx->std |= V4L2_STD_SECAM_L;
|
||||
} else {
|
||||
CX18_INFO("No tuner detected, default to NTSC-M\n");
|
||||
cx->std |= V4L2_STD_NTSC_M;
|
||||
}
|
||||
}
|
||||
|
||||
static v4l2_std_id cx18_parse_std(struct cx18 *cx)
|
||||
{
|
||||
switch (pal[0]) {
|
||||
case '6':
|
||||
return V4L2_STD_PAL_60;
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'g':
|
||||
case 'G':
|
||||
return V4L2_STD_PAL_BG;
|
||||
case 'h':
|
||||
case 'H':
|
||||
return V4L2_STD_PAL_H;
|
||||
case 'n':
|
||||
case 'N':
|
||||
if (pal[1] == 'c' || pal[1] == 'C')
|
||||
return V4L2_STD_PAL_Nc;
|
||||
return V4L2_STD_PAL_N;
|
||||
case 'i':
|
||||
case 'I':
|
||||
return V4L2_STD_PAL_I;
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'k':
|
||||
case 'K':
|
||||
return V4L2_STD_PAL_DK;
|
||||
case 'M':
|
||||
case 'm':
|
||||
return V4L2_STD_PAL_M;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
CX18_WARN("pal= argument not recognised\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (secam[0]) {
|
||||
case 'b':
|
||||
case 'B':
|
||||
case 'g':
|
||||
case 'G':
|
||||
case 'h':
|
||||
case 'H':
|
||||
return V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H;
|
||||
case 'd':
|
||||
case 'D':
|
||||
case 'k':
|
||||
case 'K':
|
||||
return V4L2_STD_SECAM_DK;
|
||||
case 'l':
|
||||
case 'L':
|
||||
if (secam[1] == 'C' || secam[1] == 'c')
|
||||
return V4L2_STD_SECAM_LC;
|
||||
return V4L2_STD_SECAM_L;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
CX18_WARN("secam= argument not recognised\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (ntsc[0]) {
|
||||
case 'm':
|
||||
case 'M':
|
||||
return V4L2_STD_NTSC_M;
|
||||
case 'j':
|
||||
case 'J':
|
||||
return V4L2_STD_NTSC_M_JP;
|
||||
case 'k':
|
||||
case 'K':
|
||||
return V4L2_STD_NTSC_M_KR;
|
||||
case '-':
|
||||
break;
|
||||
default:
|
||||
CX18_WARN("ntsc= argument not recognised\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* no match found */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cx18_process_options(struct cx18 *cx)
|
||||
{
|
||||
int i, j;
|
||||
|
||||
cx->options.megabytes[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_buffers;
|
||||
cx->options.megabytes[CX18_ENC_STREAM_TYPE_TS] = enc_ts_buffers;
|
||||
cx->options.megabytes[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_buffers;
|
||||
cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] = enc_vbi_buffers;
|
||||
cx->options.megabytes[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_buffers;
|
||||
cx->options.cardtype = cardtype[cx->num];
|
||||
cx->options.tuner = tuner[cx->num];
|
||||
cx->options.radio = radio[cx->num];
|
||||
|
||||
cx->std = cx18_parse_std(cx);
|
||||
if (cx->options.cardtype == -1) {
|
||||
CX18_INFO("Ignore card\n");
|
||||
return;
|
||||
}
|
||||
cx->card = cx18_get_card(cx->options.cardtype - 1);
|
||||
if (cx->card)
|
||||
CX18_INFO("User specified %s card\n", cx->card->name);
|
||||
else if (cx->options.cardtype != 0)
|
||||
CX18_ERR("Unknown user specified type, trying to autodetect card\n");
|
||||
if (cx->card == NULL) {
|
||||
if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
|
||||
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
|
||||
CX18_INFO("Autodetected Hauppauge card\n");
|
||||
}
|
||||
}
|
||||
if (cx->card == NULL) {
|
||||
for (i = 0; (cx->card = cx18_get_card(i)); i++) {
|
||||
if (cx->card->pci_list == NULL)
|
||||
continue;
|
||||
for (j = 0; cx->card->pci_list[j].device; j++) {
|
||||
if (cx->dev->device !=
|
||||
cx->card->pci_list[j].device)
|
||||
continue;
|
||||
if (cx->dev->subsystem_vendor !=
|
||||
cx->card->pci_list[j].subsystem_vendor)
|
||||
continue;
|
||||
if (cx->dev->subsystem_device !=
|
||||
cx->card->pci_list[j].subsystem_device)
|
||||
continue;
|
||||
CX18_INFO("Autodetected %s card\n", cx->card->name);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
}
|
||||
done:
|
||||
|
||||
if (cx->card == NULL) {
|
||||
cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
|
||||
CX18_ERR("Unknown card: vendor/device: %04x/%04x\n",
|
||||
cx->dev->vendor, cx->dev->device);
|
||||
CX18_ERR(" subsystem vendor/device: %04x/%04x\n",
|
||||
cx->dev->subsystem_vendor, cx->dev->subsystem_device);
|
||||
CX18_ERR("Defaulting to %s card\n", cx->card->name);
|
||||
CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
|
||||
CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
|
||||
CX18_ERR("Prefix your subject line with [UNKNOWN CX18 CARD].\n");
|
||||
}
|
||||
cx->v4l2_cap = cx->card->v4l2_capabilities;
|
||||
cx->card_name = cx->card->name;
|
||||
cx->card_i2c = cx->card->i2c;
|
||||
}
|
||||
|
||||
/* Precondition: the cx18 structure has been memset to 0. Only
|
||||
the dev and num fields have been filled in.
|
||||
No assumptions on the card type may be made here (see cx18_init_struct2
|
||||
for that).
|
||||
*/
|
||||
static int __devinit cx18_init_struct1(struct cx18 *cx)
|
||||
{
|
||||
cx->base_addr = pci_resource_start(cx->dev, 0);
|
||||
|
||||
mutex_init(&cx->serialize_lock);
|
||||
mutex_init(&cx->i2c_bus_lock[0]);
|
||||
mutex_init(&cx->i2c_bus_lock[1]);
|
||||
|
||||
spin_lock_init(&cx->lock);
|
||||
spin_lock_init(&cx->dma_reg_lock);
|
||||
|
||||
/* start counting open_id at 1 */
|
||||
cx->open_id = 1;
|
||||
|
||||
/* Initial settings */
|
||||
cx2341x_fill_defaults(&cx->params);
|
||||
cx->temporal_strength = cx->params.video_temporal_filter;
|
||||
cx->spatial_strength = cx->params.video_spatial_filter;
|
||||
cx->filter_mode = cx->params.video_spatial_filter_mode |
|
||||
(cx->params.video_temporal_filter_mode << 1) |
|
||||
(cx->params.video_median_filter_type << 2);
|
||||
cx->params.port = CX2341X_PORT_MEMORY;
|
||||
cx->params.capabilities = CX2341X_CAP_HAS_SLICED_VBI;
|
||||
init_waitqueue_head(&cx->cap_w);
|
||||
init_waitqueue_head(&cx->mb_apu_waitq);
|
||||
init_waitqueue_head(&cx->mb_cpu_waitq);
|
||||
init_waitqueue_head(&cx->mb_epu_waitq);
|
||||
init_waitqueue_head(&cx->mb_hpu_waitq);
|
||||
init_waitqueue_head(&cx->dma_waitq);
|
||||
|
||||
/* VBI */
|
||||
cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
|
||||
cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
|
||||
cx->vbi.raw_size = 1456;
|
||||
cx->vbi.raw_decoder_line_size = 1456;
|
||||
cx->vbi.raw_decoder_sav_odd_field = 0x20;
|
||||
cx->vbi.raw_decoder_sav_even_field = 0x60;
|
||||
cx->vbi.sliced_decoder_line_size = 272;
|
||||
cx->vbi.sliced_decoder_sav_odd_field = 0xB0;
|
||||
cx->vbi.sliced_decoder_sav_even_field = 0xF0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Second initialization part. Here the card type has been
|
||||
autodetected. */
|
||||
static void __devinit cx18_init_struct2(struct cx18 *cx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CX18_CARD_MAX_VIDEO_INPUTS; i++)
|
||||
if (cx->card->video_inputs[i].video_type == 0)
|
||||
break;
|
||||
cx->nof_inputs = i;
|
||||
for (i = 0; i < CX18_CARD_MAX_AUDIO_INPUTS; i++)
|
||||
if (cx->card->audio_inputs[i].audio_type == 0)
|
||||
break;
|
||||
cx->nof_audio_inputs = i;
|
||||
|
||||
/* Find tuner input */
|
||||
for (i = 0; i < cx->nof_inputs; i++) {
|
||||
if (cx->card->video_inputs[i].video_type ==
|
||||
CX18_CARD_INPUT_VID_TUNER)
|
||||
break;
|
||||
}
|
||||
if (i == cx->nof_inputs)
|
||||
i = 0;
|
||||
cx->active_input = i;
|
||||
cx->audio_input = cx->card->video_inputs[i].audio_index;
|
||||
cx->av_state.vid_input = CX18_AV_COMPOSITE7;
|
||||
cx->av_state.aud_input = CX18_AV_AUDIO8;
|
||||
cx->av_state.audclk_freq = 48000;
|
||||
cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
|
||||
cx->av_state.vbi_line_offset = 8;
|
||||
}
|
||||
|
||||
static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
u16 cmd;
|
||||
unsigned char pci_latency;
|
||||
|
||||
CX18_DEBUG_INFO("Enabling pci device\n");
|
||||
|
||||
if (pci_enable_device(dev)) {
|
||||
CX18_ERR("Can't enable device %d!\n", cx->num);
|
||||
return -EIO;
|
||||
}
|
||||
if (pci_set_dma_mask(dev, 0xffffffff)) {
|
||||
CX18_ERR("No suitable DMA available on card %d.\n", cx->num);
|
||||
return -EIO;
|
||||
}
|
||||
if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
|
||||
CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Check for bus mastering */
|
||||
pci_read_config_word(dev, PCI_COMMAND, &cmd);
|
||||
cmd |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
|
||||
pci_write_config_word(dev, PCI_COMMAND, cmd);
|
||||
|
||||
pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev);
|
||||
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
|
||||
|
||||
if (pci_latency < 64 && cx18_pci_latency) {
|
||||
CX18_INFO("Unreasonably low latency timer, "
|
||||
"setting to 64 (was %d)\n", pci_latency);
|
||||
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
|
||||
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
|
||||
}
|
||||
/* This config space value relates to DMA latencies. The
|
||||
default value 0x8080 is too low however and will lead
|
||||
to DMA errors. 0xffff is the max value which solves
|
||||
these problems. */
|
||||
pci_write_config_dword(dev, 0x40, 0xffff);
|
||||
|
||||
CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
|
||||
"irq: %d, latency: %d, memory: 0x%lx\n",
|
||||
cx->dev->device, cx->card_rev, dev->bus->number,
|
||||
PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
|
||||
cx->dev->irq, pci_latency, (unsigned long)cx->base_addr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 cx18_request_module(struct cx18 *cx, u32 hw,
|
||||
const char *name, u32 id)
|
||||
{
|
||||
if ((hw & id) == 0)
|
||||
return hw;
|
||||
if (request_module(name) != 0) {
|
||||
CX18_ERR("Failed to load module %s\n", name);
|
||||
return hw & ~id;
|
||||
}
|
||||
CX18_DEBUG_INFO("Loaded module %s\n", name);
|
||||
return hw;
|
||||
}
|
||||
|
||||
static void cx18_load_and_init_modules(struct cx18 *cx)
|
||||
{
|
||||
u32 hw = cx->card->hw_all;
|
||||
int i;
|
||||
|
||||
/* load modules */
|
||||
#ifndef CONFIG_VIDEO_TUNER
|
||||
hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER);
|
||||
#endif
|
||||
#ifndef CONFIG_VIDEO_CS5345
|
||||
hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345);
|
||||
#endif
|
||||
|
||||
/* check which i2c devices are actually found */
|
||||
for (i = 0; i < 32; i++) {
|
||||
u32 device = 1 << i;
|
||||
|
||||
if (!(device & hw))
|
||||
continue;
|
||||
if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
|
||||
device == CX18_HW_CX23418 || device == CX18_HW_DVB) {
|
||||
/* These 'devices' do not use i2c probing */
|
||||
cx->hw_flags |= device;
|
||||
continue;
|
||||
}
|
||||
cx18_i2c_register(cx, i);
|
||||
if (cx18_i2c_hw_addr(cx, device) > 0)
|
||||
cx->hw_flags |= device;
|
||||
}
|
||||
|
||||
hw = cx->hw_flags;
|
||||
}
|
||||
|
||||
static int __devinit cx18_probe(struct pci_dev *dev,
|
||||
const struct pci_device_id *pci_id)
|
||||
{
|
||||
int retval = 0;
|
||||
int vbi_buf_size;
|
||||
u32 devtype;
|
||||
struct cx18 *cx;
|
||||
|
||||
spin_lock(&cx18_cards_lock);
|
||||
|
||||
/* Make sure we've got a place for this card */
|
||||
if (cx18_cards_active == CX18_MAX_CARDS) {
|
||||
printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n",
|
||||
cx18_cards_active);
|
||||
spin_unlock(&cx18_cards_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
|
||||
if (cx == 0) {
|
||||
spin_unlock(&cx18_cards_lock);
|
||||
return -ENOMEM;
|
||||
}
|
||||
cx18_cards[cx18_cards_active] = cx;
|
||||
cx->dev = dev;
|
||||
cx->num = cx18_cards_active++;
|
||||
snprintf(cx->name, sizeof(cx->name) - 1, "cx18-%d", cx->num);
|
||||
CX18_INFO("Initializing card #%d\n", cx->num);
|
||||
|
||||
spin_unlock(&cx18_cards_lock);
|
||||
|
||||
cx18_process_options(cx);
|
||||
if (cx->options.cardtype == -1) {
|
||||
retval = -ENODEV;
|
||||
goto err;
|
||||
}
|
||||
if (cx18_init_struct1(cx)) {
|
||||
retval = -ENOMEM;
|
||||
goto err;
|
||||
}
|
||||
|
||||
CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr);
|
||||
|
||||
/* PCI Device Setup */
|
||||
retval = cx18_setup_pci(cx, dev, pci_id);
|
||||
if (retval != 0) {
|
||||
if (retval == -EIO)
|
||||
goto free_workqueue;
|
||||
else if (retval == -ENXIO)
|
||||
goto free_mem;
|
||||
}
|
||||
/* save cx in the pci struct for later use */
|
||||
pci_set_drvdata(dev, cx);
|
||||
|
||||
/* map io memory */
|
||||
CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
|
||||
cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
|
||||
cx->enc_mem = ioremap_nocache(cx->base_addr + CX18_MEM_OFFSET,
|
||||
CX18_MEM_SIZE);
|
||||
if (!cx->enc_mem) {
|
||||
CX18_ERR("ioremap failed, perhaps increasing __VMALLOC_RESERVE in page.h\n");
|
||||
CX18_ERR("or disabling CONFIG_HIGHMEM4G into the kernel would help\n");
|
||||
retval = -ENOMEM;
|
||||
goto free_mem;
|
||||
}
|
||||
cx->reg_mem = cx->enc_mem + CX18_REG_OFFSET;
|
||||
devtype = read_reg(0xC72028);
|
||||
switch (devtype & 0xff000000) {
|
||||
case 0xff000000:
|
||||
CX18_INFO("cx23418 revision %08x (A)\n", devtype);
|
||||
break;
|
||||
case 0x01000000:
|
||||
CX18_INFO("cx23418 revision %08x (B)\n", devtype);
|
||||
break;
|
||||
default:
|
||||
CX18_INFO("cx23418 revision %08x (Unknown)\n", devtype);
|
||||
break;
|
||||
}
|
||||
|
||||
cx18_init_power(cx, 1);
|
||||
cx18_init_memory(cx);
|
||||
|
||||
cx->scb = (struct cx18_scb *)(cx->enc_mem + SCB_OFFSET);
|
||||
cx18_init_scb(cx);
|
||||
|
||||
cx18_gpio_init(cx);
|
||||
|
||||
/* active i2c */
|
||||
CX18_DEBUG_INFO("activating i2c...\n");
|
||||
if (init_cx18_i2c(cx)) {
|
||||
CX18_ERR("Could not initialize i2c\n");
|
||||
goto free_map;
|
||||
}
|
||||
|
||||
CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active);
|
||||
|
||||
if (cx->card->hw_all & CX18_HW_TVEEPROM) {
|
||||
/* Based on the model number the cardtype may be changed.
|
||||
The PCI IDs are not always reliable. */
|
||||
cx18_process_eeprom(cx);
|
||||
}
|
||||
if (cx->card->comment)
|
||||
CX18_INFO("%s", cx->card->comment);
|
||||
if (cx->card->v4l2_capabilities == 0) {
|
||||
retval = -ENODEV;
|
||||
goto free_i2c;
|
||||
}
|
||||
cx18_init_memory(cx);
|
||||
|
||||
/* Register IRQ */
|
||||
retval = request_irq(cx->dev->irq, cx18_irq_handler,
|
||||
IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx);
|
||||
if (retval) {
|
||||
CX18_ERR("Failed to register irq %d\n", retval);
|
||||
goto free_i2c;
|
||||
}
|
||||
|
||||
if (cx->std == 0)
|
||||
cx->std = V4L2_STD_NTSC_M;
|
||||
|
||||
if (cx->options.tuner == -1) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < CX18_CARD_MAX_TUNERS; i++) {
|
||||
if ((cx->std & cx->card->tuners[i].std) == 0)
|
||||
continue;
|
||||
cx->options.tuner = cx->card->tuners[i].tuner;
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* if no tuner was found, then pick the first tuner in the card list */
|
||||
if (cx->options.tuner == -1 && cx->card->tuners[0].std) {
|
||||
cx->std = cx->card->tuners[0].std;
|
||||
cx->options.tuner = cx->card->tuners[0].tuner;
|
||||
}
|
||||
if (cx->options.radio == -1)
|
||||
cx->options.radio = (cx->card->radio_input.audio_type != 0);
|
||||
|
||||
/* The card is now fully identified, continue with card-specific
|
||||
initialization. */
|
||||
cx18_init_struct2(cx);
|
||||
|
||||
cx18_load_and_init_modules(cx);
|
||||
|
||||
if (cx->std & V4L2_STD_525_60) {
|
||||
cx->is_60hz = 1;
|
||||
cx->is_out_60hz = 1;
|
||||
} else {
|
||||
cx->is_50hz = 1;
|
||||
cx->is_out_50hz = 1;
|
||||
}
|
||||
cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
|
||||
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = 0x08000;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_TS] = 0x08000;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = 0x01200;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = 0x20000;
|
||||
vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
|
||||
cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
|
||||
|
||||
if (cx->options.radio > 0)
|
||||
cx->v4l2_cap |= V4L2_CAP_RADIO;
|
||||
|
||||
retval = cx18_streams_setup(cx);
|
||||
if (retval) {
|
||||
CX18_ERR("Error %d setting up streams\n", retval);
|
||||
goto free_irq;
|
||||
}
|
||||
retval = cx18_streams_register(cx);
|
||||
if (retval) {
|
||||
CX18_ERR("Error %d registering devices\n", retval);
|
||||
goto free_streams;
|
||||
}
|
||||
|
||||
if (cx->options.tuner > -1) {
|
||||
struct tuner_setup setup;
|
||||
|
||||
setup.addr = ADDR_UNSET;
|
||||
setup.type = cx->options.tuner;
|
||||
setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
|
||||
setup.tuner_callback = (setup.type == TUNER_XC2028) ?
|
||||
cx18_reset_tuner_gpio : NULL;
|
||||
cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup);
|
||||
if (setup.type == TUNER_XC2028) {
|
||||
static struct xc2028_ctrl ctrl = {
|
||||
.fname = XC2028_DEFAULT_FIRMWARE,
|
||||
.max_len = 64,
|
||||
};
|
||||
struct v4l2_priv_tun_config cfg = {
|
||||
.tuner = cx->options.tuner,
|
||||
.priv = &ctrl,
|
||||
};
|
||||
cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg);
|
||||
}
|
||||
}
|
||||
|
||||
/* The tuner is fixed to the standard. The other inputs (e.g. S-Video)
|
||||
are not. */
|
||||
cx->tuner_std = cx->std;
|
||||
|
||||
cx18_init_on_first_open(cx);
|
||||
|
||||
CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name);
|
||||
|
||||
return 0;
|
||||
|
||||
free_streams:
|
||||
cx18_streams_cleanup(cx);
|
||||
free_irq:
|
||||
free_irq(cx->dev->irq, (void *)cx);
|
||||
free_i2c:
|
||||
exit_cx18_i2c(cx);
|
||||
free_map:
|
||||
cx18_iounmap(cx);
|
||||
free_mem:
|
||||
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
|
||||
free_workqueue:
|
||||
err:
|
||||
if (retval == 0)
|
||||
retval = -ENODEV;
|
||||
CX18_ERR("Error %d on initialization\n", retval);
|
||||
|
||||
kfree(cx18_cards[cx18_cards_active]);
|
||||
cx18_cards[cx18_cards_active] = NULL;
|
||||
return retval;
|
||||
}
|
||||
|
||||
int cx18_init_on_first_open(struct cx18 *cx)
|
||||
{
|
||||
int video_input;
|
||||
int fw_retry_count = 3;
|
||||
struct v4l2_frequency vf;
|
||||
|
||||
if (test_bit(CX18_F_I_FAILED, &cx->i_flags))
|
||||
return -ENXIO;
|
||||
|
||||
if (test_and_set_bit(CX18_F_I_INITED, &cx->i_flags))
|
||||
return 0;
|
||||
|
||||
while (--fw_retry_count > 0) {
|
||||
/* load firmware */
|
||||
if (cx18_firmware_init(cx) == 0)
|
||||
break;
|
||||
if (fw_retry_count > 1)
|
||||
CX18_WARN("Retry loading firmware\n");
|
||||
}
|
||||
|
||||
if (fw_retry_count == 0) {
|
||||
set_bit(CX18_F_I_FAILED, &cx->i_flags);
|
||||
return -ENXIO;
|
||||
}
|
||||
set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
|
||||
|
||||
/* Init the firmware twice to work around a silicon bug
|
||||
* transport related. */
|
||||
|
||||
fw_retry_count = 3;
|
||||
while (--fw_retry_count > 0) {
|
||||
/* load firmware */
|
||||
if (cx18_firmware_init(cx) == 0)
|
||||
break;
|
||||
if (fw_retry_count > 1)
|
||||
CX18_WARN("Retry loading firmware\n");
|
||||
}
|
||||
|
||||
if (fw_retry_count == 0) {
|
||||
set_bit(CX18_F_I_FAILED, &cx->i_flags);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
vf.tuner = 0;
|
||||
vf.type = V4L2_TUNER_ANALOG_TV;
|
||||
vf.frequency = 6400; /* the tuner 'baseline' frequency */
|
||||
|
||||
/* Set initial frequency. For PAL/SECAM broadcasts no
|
||||
'default' channel exists AFAIK. */
|
||||
if (cx->std == V4L2_STD_NTSC_M_JP)
|
||||
vf.frequency = 1460; /* ch. 1 91250*16/1000 */
|
||||
else if (cx->std & V4L2_STD_NTSC_M)
|
||||
vf.frequency = 1076; /* ch. 4 67250*16/1000 */
|
||||
|
||||
video_input = cx->active_input;
|
||||
cx->active_input++; /* Force update of input */
|
||||
cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_INPUT, &video_input);
|
||||
|
||||
/* Let the VIDIOC_S_STD ioctl do all the work, keeps the code
|
||||
in one place. */
|
||||
cx->std++; /* Force full standard initialization */
|
||||
cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_STD, &cx->tuner_std);
|
||||
cx18_v4l2_ioctls(cx, NULL, VIDIOC_S_FREQUENCY, &vf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cx18_remove(struct pci_dev *pci_dev)
|
||||
{
|
||||
struct cx18 *cx = pci_get_drvdata(pci_dev);
|
||||
|
||||
CX18_DEBUG_INFO("Removing Card #%d\n", cx->num);
|
||||
|
||||
/* Stop all captures */
|
||||
CX18_DEBUG_INFO("Stopping all streams\n");
|
||||
if (atomic_read(&cx->capturing) > 0)
|
||||
cx18_stop_all_captures(cx);
|
||||
|
||||
/* Interrupts */
|
||||
sw1_irq_disable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
|
||||
sw2_irq_disable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
|
||||
|
||||
cx18_halt_firmware(cx);
|
||||
|
||||
cx18_streams_cleanup(cx);
|
||||
|
||||
exit_cx18_i2c(cx);
|
||||
|
||||
free_irq(cx->dev->irq, (void *)cx);
|
||||
|
||||
if (cx->dev)
|
||||
cx18_iounmap(cx);
|
||||
|
||||
release_mem_region(cx->base_addr, CX18_MEM_SIZE);
|
||||
|
||||
pci_disable_device(cx->dev);
|
||||
|
||||
CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
|
||||
}
|
||||
|
||||
/* define a pci_driver for card detection */
|
||||
static struct pci_driver cx18_pci_driver = {
|
||||
.name = "cx18",
|
||||
.id_table = cx18_pci_tbl,
|
||||
.probe = cx18_probe,
|
||||
.remove = cx18_remove,
|
||||
};
|
||||
|
||||
static int module_start(void)
|
||||
{
|
||||
printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION);
|
||||
|
||||
memset(cx18_cards, 0, sizeof(cx18_cards));
|
||||
|
||||
/* Validate parameters */
|
||||
if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
|
||||
printk(KERN_ERR "cx18: Exiting, ivtv_first_minor must be between 0 and %d\n",
|
||||
CX18_MAX_CARDS - 1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (cx18_debug < 0 || cx18_debug > 511) {
|
||||
cx18_debug = 0;
|
||||
printk(KERN_INFO "cx18: Debug value must be >= 0 and <= 511!\n");
|
||||
}
|
||||
|
||||
if (pci_register_driver(&cx18_pci_driver)) {
|
||||
printk(KERN_ERR "cx18: Error detecting PCI card\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
printk(KERN_INFO "cx18: End initialization\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void module_cleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
pci_unregister_driver(&cx18_pci_driver);
|
||||
|
||||
for (i = 0; i < cx18_cards_active; i++) {
|
||||
if (cx18_cards[i] == NULL)
|
||||
continue;
|
||||
kfree(cx18_cards[i]);
|
||||
}
|
||||
}
|
||||
|
||||
module_init(module_start);
|
||||
module_exit(module_cleanup);
|
|
@ -0,0 +1,500 @@
|
|||
/*
|
||||
* cx18 driver internal defines and structures
|
||||
*
|
||||
* Derived from ivtv-driver.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef CX18_DRIVER_H
|
||||
#define CX18_DRIVER_H
|
||||
|
||||
#include <linux/version.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/i2c-algo-bit.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/byteorder/swab.h>
|
||||
#include <linux/pagemap.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#include <linux/dvb/video.h>
|
||||
#include <linux/dvb/audio.h>
|
||||
#include <media/v4l2-common.h>
|
||||
#include <media/tuner.h>
|
||||
#include "cx18-mailbox.h"
|
||||
#include "cx18-av-core.h"
|
||||
#include "cx23418.h"
|
||||
|
||||
/* DVB */
|
||||
#include "demux.h"
|
||||
#include "dmxdev.h"
|
||||
#include "dvb_demux.h"
|
||||
#include "dvb_frontend.h"
|
||||
#include "dvb_net.h"
|
||||
#include "dvbdev.h"
|
||||
|
||||
#ifndef CONFIG_PCI
|
||||
# error "This driver requires kernel PCI support."
|
||||
#endif
|
||||
|
||||
#define CX18_MEM_OFFSET 0x00000000
|
||||
#define CX18_MEM_SIZE 0x04000000
|
||||
#define CX18_REG_OFFSET 0x02000000
|
||||
|
||||
/* Maximum cx18 driver instances. */
|
||||
#define CX18_MAX_CARDS 32
|
||||
|
||||
/* Supported cards */
|
||||
#define CX18_CARD_HVR_1600_ESMT 0 /* Hauppauge HVR 1600 (ESMT memory) */
|
||||
#define CX18_CARD_HVR_1600_SAMSUNG 1 /* Hauppauge HVR 1600 (Samsung memory) */
|
||||
#define CX18_CARD_COMPRO_H900 2 /* Compro VideoMate H900 */
|
||||
#define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */
|
||||
#define CX18_CARD_LAST 3
|
||||
|
||||
#define CX18_ENC_STREAM_TYPE_MPG 0
|
||||
#define CX18_ENC_STREAM_TYPE_TS 1
|
||||
#define CX18_ENC_STREAM_TYPE_YUV 2
|
||||
#define CX18_ENC_STREAM_TYPE_VBI 3
|
||||
#define CX18_ENC_STREAM_TYPE_PCM 4
|
||||
#define CX18_ENC_STREAM_TYPE_IDX 5
|
||||
#define CX18_ENC_STREAM_TYPE_RAD 6
|
||||
#define CX18_MAX_STREAMS 7
|
||||
|
||||
/* system vendor and device IDs */
|
||||
#define PCI_VENDOR_ID_CX 0x14f1
|
||||
#define PCI_DEVICE_ID_CX23418 0x5b7a
|
||||
|
||||
/* subsystem vendor ID */
|
||||
#define CX18_PCI_ID_HAUPPAUGE 0x0070
|
||||
#define CX18_PCI_ID_COMPRO 0x185b
|
||||
#define CX18_PCI_ID_YUAN 0x12ab
|
||||
|
||||
/* ======================================================================== */
|
||||
/* ========================== START USER SETTABLE DMA VARIABLES =========== */
|
||||
/* ======================================================================== */
|
||||
|
||||
/* DMA Buffers, Default size in MB allocated */
|
||||
#define CX18_DEFAULT_ENC_TS_BUFFERS 1
|
||||
#define CX18_DEFAULT_ENC_MPG_BUFFERS 2
|
||||
#define CX18_DEFAULT_ENC_IDX_BUFFERS 1
|
||||
#define CX18_DEFAULT_ENC_YUV_BUFFERS 2
|
||||
#define CX18_DEFAULT_ENC_VBI_BUFFERS 1
|
||||
#define CX18_DEFAULT_ENC_PCM_BUFFERS 1
|
||||
|
||||
/* i2c stuff */
|
||||
#define I2C_CLIENTS_MAX 16
|
||||
|
||||
/* debugging */
|
||||
|
||||
/* Flag to turn on high volume debugging */
|
||||
#define CX18_DBGFLG_WARN (1 << 0)
|
||||
#define CX18_DBGFLG_INFO (1 << 1)
|
||||
#define CX18_DBGFLG_API (1 << 2)
|
||||
#define CX18_DBGFLG_DMA (1 << 3)
|
||||
#define CX18_DBGFLG_IOCTL (1 << 4)
|
||||
#define CX18_DBGFLG_FILE (1 << 5)
|
||||
#define CX18_DBGFLG_I2C (1 << 6)
|
||||
#define CX18_DBGFLG_IRQ (1 << 7)
|
||||
/* Flag to turn on high volume debugging */
|
||||
#define CX18_DBGFLG_HIGHVOL (1 << 8)
|
||||
|
||||
/* NOTE: extra space before comma in 'cx->num , ## args' is required for
|
||||
gcc-2.95, otherwise it won't compile. */
|
||||
#define CX18_DEBUG(x, type, fmt, args...) \
|
||||
do { \
|
||||
if ((x) & cx18_debug) \
|
||||
printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \
|
||||
} while (0)
|
||||
#define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args)
|
||||
#define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args)
|
||||
#define CX18_DEBUG_API(fmt, args...) CX18_DEBUG(CX18_DBGFLG_API, "api", fmt , ## args)
|
||||
#define CX18_DEBUG_DMA(fmt, args...) CX18_DEBUG(CX18_DBGFLG_DMA, "dma", fmt , ## args)
|
||||
#define CX18_DEBUG_IOCTL(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
|
||||
#define CX18_DEBUG_FILE(fmt, args...) CX18_DEBUG(CX18_DBGFLG_FILE, "file", fmt , ## args)
|
||||
#define CX18_DEBUG_I2C(fmt, args...) CX18_DEBUG(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
|
||||
#define CX18_DEBUG_IRQ(fmt, args...) CX18_DEBUG(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
|
||||
|
||||
#define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \
|
||||
do { \
|
||||
if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
|
||||
printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \
|
||||
} while (0)
|
||||
#define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args)
|
||||
#define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args)
|
||||
#define CX18_DEBUG_HI_API(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_API, "api", fmt , ## args)
|
||||
#define CX18_DEBUG_HI_DMA(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_DMA, "dma", fmt , ## args)
|
||||
#define CX18_DEBUG_HI_IOCTL(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IOCTL, "ioctl", fmt , ## args)
|
||||
#define CX18_DEBUG_HI_FILE(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_FILE, "file", fmt , ## args)
|
||||
#define CX18_DEBUG_HI_I2C(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_I2C, "i2c", fmt , ## args)
|
||||
#define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
|
||||
|
||||
/* Standard kernel messages */
|
||||
#define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args)
|
||||
#define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args)
|
||||
#define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args)
|
||||
|
||||
/* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
|
||||
#define MPEG_FRAME_TYPE_IFRAME 1
|
||||
#define MPEG_FRAME_TYPE_IFRAME_PFRAME 3
|
||||
#define MPEG_FRAME_TYPE_ALL 7
|
||||
|
||||
#define CX18_MAX_PGM_INDEX (400)
|
||||
|
||||
extern int cx18_debug;
|
||||
|
||||
|
||||
struct cx18_options {
|
||||
int megabytes[CX18_MAX_STREAMS]; /* Size in megabytes of each stream */
|
||||
int cardtype; /* force card type on load */
|
||||
int tuner; /* set tuner on load */
|
||||
int radio; /* enable/disable radio */
|
||||
};
|
||||
|
||||
/* per-buffer bit flags */
|
||||
#define CX18_F_B_NEED_BUF_SWAP 0 /* this buffer should be byte swapped */
|
||||
|
||||
/* per-stream, s_flags */
|
||||
#define CX18_F_S_CLAIMED 3 /* this stream is claimed */
|
||||
#define CX18_F_S_STREAMING 4 /* the fw is decoding/encoding this stream */
|
||||
#define CX18_F_S_INTERNAL_USE 5 /* this stream is used internally (sliced VBI processing) */
|
||||
#define CX18_F_S_STREAMOFF 7 /* signal end of stream EOS */
|
||||
#define CX18_F_S_APPL_IO 8 /* this stream is used read/written by an application */
|
||||
|
||||
/* per-cx18, i_flags */
|
||||
#define CX18_F_I_LOADED_FW 0 /* Loaded the firmware the first time */
|
||||
#define CX18_F_I_EOS 4 /* End of encoder stream reached */
|
||||
#define CX18_F_I_RADIO_USER 5 /* The radio tuner is selected */
|
||||
#define CX18_F_I_ENC_PAUSED 13 /* the encoder is paused */
|
||||
#define CX18_F_I_INITED 21 /* set after first open */
|
||||
#define CX18_F_I_FAILED 22 /* set if first open failed */
|
||||
|
||||
/* These are the VBI types as they appear in the embedded VBI private packets. */
|
||||
#define CX18_SLICED_TYPE_TELETEXT_B (1)
|
||||
#define CX18_SLICED_TYPE_CAPTION_525 (4)
|
||||
#define CX18_SLICED_TYPE_WSS_625 (5)
|
||||
#define CX18_SLICED_TYPE_VPS (7)
|
||||
|
||||
struct cx18_buffer {
|
||||
struct list_head list;
|
||||
dma_addr_t dma_handle;
|
||||
u32 id;
|
||||
unsigned long b_flags;
|
||||
char *buf;
|
||||
|
||||
u32 bytesused;
|
||||
u32 readpos;
|
||||
};
|
||||
|
||||
struct cx18_queue {
|
||||
struct list_head list;
|
||||
u32 buffers;
|
||||
u32 length;
|
||||
u32 bytesused;
|
||||
};
|
||||
|
||||
struct cx18_dvb {
|
||||
struct dmx_frontend hw_frontend;
|
||||
struct dmx_frontend mem_frontend;
|
||||
struct dmxdev dmxdev;
|
||||
struct dvb_adapter dvb_adapter;
|
||||
struct dvb_demux demux;
|
||||
struct dvb_frontend *fe;
|
||||
struct dvb_net dvbnet;
|
||||
int enabled;
|
||||
int feeding;
|
||||
|
||||
struct mutex feedlock;
|
||||
|
||||
};
|
||||
|
||||
struct cx18; /* forward reference */
|
||||
struct cx18_scb; /* forward reference */
|
||||
|
||||
struct cx18_stream {
|
||||
/* These first four fields are always set, even if the stream
|
||||
is not actually created. */
|
||||
struct video_device *v4l2dev; /* NULL when stream not created */
|
||||
struct cx18 *cx; /* for ease of use */
|
||||
const char *name; /* name of the stream */
|
||||
int type; /* stream type */
|
||||
u32 handle; /* task handle */
|
||||
unsigned mdl_offset;
|
||||
|
||||
u32 id;
|
||||
spinlock_t qlock; /* locks access to the queues */
|
||||
unsigned long s_flags; /* status flags, see above */
|
||||
int dma; /* can be PCI_DMA_TODEVICE,
|
||||
PCI_DMA_FROMDEVICE or
|
||||
PCI_DMA_NONE */
|
||||
u64 dma_pts;
|
||||
wait_queue_head_t waitq;
|
||||
|
||||
/* Buffer Stats */
|
||||
u32 buffers;
|
||||
u32 buf_size;
|
||||
u32 buffers_stolen;
|
||||
|
||||
/* Buffer Queues */
|
||||
struct cx18_queue q_free; /* free buffers */
|
||||
struct cx18_queue q_full; /* full buffers */
|
||||
struct cx18_queue q_io; /* waiting for I/O */
|
||||
|
||||
/* DVB / Digital Transport */
|
||||
struct cx18_dvb dvb;
|
||||
};
|
||||
|
||||
struct cx18_open_id {
|
||||
u32 open_id;
|
||||
int type;
|
||||
enum v4l2_priority prio;
|
||||
struct cx18 *cx;
|
||||
};
|
||||
|
||||
/* forward declaration of struct defined in cx18-cards.h */
|
||||
struct cx18_card;
|
||||
|
||||
|
||||
#define CX18_VBI_FRAMES 32
|
||||
|
||||
/* VBI data */
|
||||
struct vbi_info {
|
||||
u32 enc_size;
|
||||
u32 frame;
|
||||
u8 cc_data_odd[256];
|
||||
u8 cc_data_even[256];
|
||||
int cc_pos;
|
||||
u8 cc_no_update;
|
||||
u8 vps[5];
|
||||
u8 vps_found;
|
||||
int wss;
|
||||
u8 wss_found;
|
||||
u8 wss_no_update;
|
||||
u32 raw_decoder_line_size;
|
||||
u8 raw_decoder_sav_odd_field;
|
||||
u8 raw_decoder_sav_even_field;
|
||||
u32 sliced_decoder_line_size;
|
||||
u8 sliced_decoder_sav_odd_field;
|
||||
u8 sliced_decoder_sav_even_field;
|
||||
struct v4l2_format in;
|
||||
/* convenience pointer to sliced struct in vbi_in union */
|
||||
struct v4l2_sliced_vbi_format *sliced_in;
|
||||
u32 service_set_in;
|
||||
int insert_mpeg;
|
||||
|
||||
/* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
|
||||
One for /dev/vbi0 and one for /dev/vbi8 */
|
||||
struct v4l2_sliced_vbi_data sliced_data[36];
|
||||
|
||||
/* Buffer for VBI data inserted into MPEG stream.
|
||||
The first byte is a dummy byte that's never used.
|
||||
The next 16 bytes contain the MPEG header for the VBI data,
|
||||
the remainder is the actual VBI data.
|
||||
The max size accepted by the MPEG VBI reinsertion turns out
|
||||
to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
|
||||
where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
|
||||
a single line header byte and 2 * 18 is the number of VBI lines per frame.
|
||||
|
||||
However, it seems that the data must be 1K aligned, so we have to
|
||||
pad the data until the 1 or 2 K boundary.
|
||||
|
||||
This pointer array will allocate 2049 bytes to store each VBI frame. */
|
||||
u8 *sliced_mpeg_data[CX18_VBI_FRAMES];
|
||||
u32 sliced_mpeg_size[CX18_VBI_FRAMES];
|
||||
struct cx18_buffer sliced_mpeg_buf;
|
||||
u32 inserted_frame;
|
||||
|
||||
u32 start[2], count;
|
||||
u32 raw_size;
|
||||
u32 sliced_size;
|
||||
};
|
||||
|
||||
/* Per cx23418, per I2C bus private algo callback data */
|
||||
struct cx18_i2c_algo_callback_data {
|
||||
struct cx18 *cx;
|
||||
int bus_index; /* 0 or 1 for the cx23418's 1st or 2nd I2C bus */
|
||||
};
|
||||
|
||||
/* Struct to hold info about cx18 cards */
|
||||
struct cx18 {
|
||||
int num; /* board number, -1 during init! */
|
||||
char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */
|
||||
struct pci_dev *dev; /* PCI device */
|
||||
const struct cx18_card *card; /* card information */
|
||||
const char *card_name; /* full name of the card */
|
||||
const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
|
||||
u8 is_50hz;
|
||||
u8 is_60hz;
|
||||
u8 is_out_50hz;
|
||||
u8 is_out_60hz;
|
||||
u8 nof_inputs; /* number of video inputs */
|
||||
u8 nof_audio_inputs; /* number of audio inputs */
|
||||
u16 buffer_id; /* buffer ID counter */
|
||||
u32 v4l2_cap; /* V4L2 capabilities of card */
|
||||
u32 hw_flags; /* Hardware description of the board */
|
||||
unsigned mdl_offset;
|
||||
struct cx18_scb *scb; /* pointer to SCB */
|
||||
|
||||
struct cx18_av_state av_state;
|
||||
|
||||
/* codec settings */
|
||||
struct cx2341x_mpeg_params params;
|
||||
u32 filter_mode;
|
||||
u32 temporal_strength;
|
||||
u32 spatial_strength;
|
||||
|
||||
/* dualwatch */
|
||||
unsigned long dualwatch_jiffies;
|
||||
u16 dualwatch_stereo_mode;
|
||||
|
||||
/* Digitizer type */
|
||||
int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
|
||||
|
||||
struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
|
||||
struct cx18_options options; /* User options */
|
||||
int stream_buf_size[CX18_MAX_STREAMS]; /* Stream buffer size */
|
||||
struct cx18_stream streams[CX18_MAX_STREAMS]; /* Stream data */
|
||||
unsigned long i_flags; /* global cx18 flags */
|
||||
atomic_t capturing; /* count number of active capture streams */
|
||||
spinlock_t lock; /* lock access to this struct */
|
||||
int search_pack_header;
|
||||
|
||||
spinlock_t dma_reg_lock; /* lock access to DMA engine registers */
|
||||
|
||||
int open_id; /* incremented each time an open occurs, used as
|
||||
unique ID. Starts at 1, so 0 can be used as
|
||||
uninitialized value in the stream->id. */
|
||||
|
||||
u32 base_addr;
|
||||
struct v4l2_prio_state prio;
|
||||
|
||||
u8 card_rev;
|
||||
void __iomem *enc_mem, *reg_mem;
|
||||
|
||||
struct vbi_info vbi;
|
||||
|
||||
u32 pgm_info_offset;
|
||||
u32 pgm_info_num;
|
||||
u32 pgm_info_write_idx;
|
||||
u32 pgm_info_read_idx;
|
||||
struct v4l2_enc_idx_entry pgm_info[CX18_MAX_PGM_INDEX];
|
||||
|
||||
u64 mpg_data_received;
|
||||
u64 vbi_data_inserted;
|
||||
|
||||
wait_queue_head_t mb_apu_waitq;
|
||||
wait_queue_head_t mb_cpu_waitq;
|
||||
wait_queue_head_t mb_epu_waitq;
|
||||
wait_queue_head_t mb_hpu_waitq;
|
||||
wait_queue_head_t cap_w;
|
||||
/* when the current DMA is finished this queue is woken up */
|
||||
wait_queue_head_t dma_waitq;
|
||||
|
||||
/* i2c */
|
||||
struct i2c_adapter i2c_adap[2];
|
||||
struct i2c_algo_bit_data i2c_algo[2];
|
||||
struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
|
||||
struct i2c_client i2c_client[2];
|
||||
struct mutex i2c_bus_lock[2];
|
||||
struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
|
||||
|
||||
/* v4l2 and User settings */
|
||||
|
||||
/* codec settings */
|
||||
u32 audio_input;
|
||||
u32 active_input;
|
||||
u32 active_output;
|
||||
v4l2_std_id std;
|
||||
v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
|
||||
};
|
||||
|
||||
/* Globals */
|
||||
extern struct cx18 *cx18_cards[];
|
||||
extern int cx18_cards_active;
|
||||
extern int cx18_first_minor;
|
||||
extern spinlock_t cx18_cards_lock;
|
||||
|
||||
/*==============Prototypes==================*/
|
||||
|
||||
/* Return non-zero if a signal is pending */
|
||||
int cx18_msleep_timeout(unsigned int msecs, int intr);
|
||||
|
||||
/* Wait on queue, returns -EINTR if interrupted */
|
||||
int cx18_waitq(wait_queue_head_t *waitq);
|
||||
|
||||
/* Read Hauppauge eeprom */
|
||||
struct tveeprom; /* forward reference */
|
||||
void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv);
|
||||
|
||||
/* First-open initialization: load firmware, etc. */
|
||||
int cx18_init_on_first_open(struct cx18 *cx);
|
||||
|
||||
/* This is a PCI post thing, where if the pci register is not read, then
|
||||
the write doesn't always take effect right away. By reading back the
|
||||
register any pending PCI writes will be performed (in order), and so
|
||||
you can be sure that the writes are guaranteed to be done.
|
||||
|
||||
Rarely needed, only in some timing sensitive cases.
|
||||
Apparently if this is not done some motherboards seem
|
||||
to kill the firmware and get into the broken state until computer is
|
||||
rebooted. */
|
||||
#define write_sync(val, reg) \
|
||||
do { writel(val, reg); readl(reg); } while (0)
|
||||
|
||||
#define read_reg(reg) readl(cx->reg_mem + (reg))
|
||||
#define write_reg(val, reg) writel(val, cx->reg_mem + (reg))
|
||||
#define write_reg_sync(val, reg) \
|
||||
do { write_reg(val, reg); read_reg(reg); } while (0)
|
||||
|
||||
#define read_enc(addr) readl(cx->enc_mem + (u32)(addr))
|
||||
#define write_enc(val, addr) writel(val, cx->enc_mem + (u32)(addr))
|
||||
#define write_enc_sync(val, addr) \
|
||||
do { write_enc(val, addr); read_enc(addr); } while (0)
|
||||
|
||||
#define sw1_irq_enable(val) do { \
|
||||
write_reg(val, SW1_INT_STATUS); \
|
||||
write_reg(read_reg(SW1_INT_ENABLE_PCI) | (val), SW1_INT_ENABLE_PCI); \
|
||||
} while (0)
|
||||
|
||||
#define sw1_irq_disable(val) \
|
||||
write_reg(read_reg(SW1_INT_ENABLE_PCI) & ~(val), SW1_INT_ENABLE_PCI);
|
||||
|
||||
#define sw2_irq_enable(val) do { \
|
||||
write_reg(val, SW2_INT_STATUS); \
|
||||
write_reg(read_reg(SW2_INT_ENABLE_PCI) | (val), SW2_INT_ENABLE_PCI); \
|
||||
} while (0)
|
||||
|
||||
#define sw2_irq_disable(val) \
|
||||
write_reg(read_reg(SW2_INT_ENABLE_PCI) & ~(val), SW2_INT_ENABLE_PCI);
|
||||
|
||||
#define setup_page(addr) do { \
|
||||
u32 val = read_reg(0xD000F8) & ~0x1f00; \
|
||||
write_reg(val | (((addr) >> 17) & 0x1f00), 0xD000F8); \
|
||||
} while (0)
|
||||
|
||||
#endif /* CX18_DRIVER_H */
|
|
@ -0,0 +1,288 @@
|
|||
/*
|
||||
* cx18 functions for DVB support
|
||||
*
|
||||
* Copyright (c) 2008 Steven Toth <stoth@hauppauge.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 "cx18-version.h"
|
||||
#include "cx18-dvb.h"
|
||||
#include "cx18-streams.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "s5h1409.h"
|
||||
|
||||
/* Wait until the MXL500X driver is merged */
|
||||
#ifdef HAVE_MXL500X
|
||||
#include "mxl500x.h"
|
||||
#endif
|
||||
|
||||
DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
|
||||
|
||||
#define CX18_REG_DMUX_NUM_PORT_0_CONTROL 0xd5a000
|
||||
|
||||
#ifdef HAVE_MXL500X
|
||||
static struct mxl500x_config hauppauge_hvr1600_tuner = {
|
||||
.delsys = MXL500x_MODE_ATSC,
|
||||
.octf = MXL500x_OCTF_CH,
|
||||
.xtal_freq = 16000000,
|
||||
.iflo_freq = 5380000,
|
||||
.ref_freq = 322800000,
|
||||
.rssi_ena = MXL_RSSI_ENABLE,
|
||||
.addr = 0xC6 >> 1,
|
||||
};
|
||||
|
||||
static struct s5h1409_config hauppauge_hvr1600_config = {
|
||||
.demod_address = 0x32 >> 1,
|
||||
.output_mode = S5H1409_SERIAL_OUTPUT,
|
||||
.gpio = S5H1409_GPIO_ON,
|
||||
.qam_if = 44000,
|
||||
.inversion = S5H1409_INVERSION_OFF,
|
||||
.status_mode = S5H1409_DEMODLOCKING,
|
||||
.mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
|
||||
|
||||
};
|
||||
#endif
|
||||
|
||||
static int dvb_register(struct cx18_stream *stream);
|
||||
|
||||
/* Kernel DVB framework calls this when the feed needs to start.
|
||||
* The CX18 framework should enable the transport DMA handling
|
||||
* and queue processing.
|
||||
*/
|
||||
static int cx18_dvb_start_feed(struct dvb_demux_feed *feed)
|
||||
{
|
||||
struct dvb_demux *demux = feed->demux;
|
||||
struct cx18_stream *stream = (struct cx18_stream *) demux->priv;
|
||||
struct cx18 *cx = stream->cx;
|
||||
int ret = -EINVAL;
|
||||
u32 v;
|
||||
|
||||
CX18_DEBUG_INFO("Start feed: pid = 0x%x index = %d\n",
|
||||
feed->pid, feed->index);
|
||||
switch (cx->card->type) {
|
||||
case CX18_CARD_HVR_1600_ESMT:
|
||||
case CX18_CARD_HVR_1600_SAMSUNG:
|
||||
v = read_reg(CX18_REG_DMUX_NUM_PORT_0_CONTROL);
|
||||
v |= 0x00400000; /* Serial Mode */
|
||||
v |= 0x00002000; /* Data Length - Byte */
|
||||
v |= 0x00010000; /* Error - Polarity */
|
||||
v |= 0x00020000; /* Error - Passthru */
|
||||
v |= 0x000c0000; /* Error - Ignore */
|
||||
write_reg(v, CX18_REG_DMUX_NUM_PORT_0_CONTROL);
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Assumption - Parallel transport - Signalling
|
||||
* undefined or default.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
if (!demux->dmx.frontend)
|
||||
return -EINVAL;
|
||||
|
||||
if (stream) {
|
||||
mutex_lock(&stream->dvb.feedlock);
|
||||
if (stream->dvb.feeding++ == 0) {
|
||||
CX18_DEBUG_INFO("Starting Transport DMA\n");
|
||||
ret = cx18_start_v4l2_encode_stream(stream);
|
||||
} else
|
||||
ret = 0;
|
||||
mutex_unlock(&stream->dvb.feedlock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Kernel DVB framework calls this when the feed needs to stop. */
|
||||
static int cx18_dvb_stop_feed(struct dvb_demux_feed *feed)
|
||||
{
|
||||
struct dvb_demux *demux = feed->demux;
|
||||
struct cx18_stream *stream = (struct cx18_stream *)demux->priv;
|
||||
struct cx18 *cx = stream->cx;
|
||||
int ret = -EINVAL;
|
||||
|
||||
CX18_DEBUG_INFO("Stop feed: pid = 0x%x index = %d\n",
|
||||
feed->pid, feed->index);
|
||||
|
||||
if (stream) {
|
||||
mutex_lock(&stream->dvb.feedlock);
|
||||
if (--stream->dvb.feeding == 0) {
|
||||
CX18_DEBUG_INFO("Stopping Transport DMA\n");
|
||||
ret = cx18_stop_v4l2_encode_stream(stream, 0);
|
||||
} else
|
||||
ret = 0;
|
||||
mutex_unlock(&stream->dvb.feedlock);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cx18_dvb_register(struct cx18_stream *stream)
|
||||
{
|
||||
struct cx18 *cx = stream->cx;
|
||||
struct cx18_dvb *dvb = &stream->dvb;
|
||||
struct dvb_adapter *dvb_adapter;
|
||||
struct dvb_demux *dvbdemux;
|
||||
struct dmx_demux *dmx;
|
||||
int ret;
|
||||
|
||||
if (!dvb)
|
||||
return -EINVAL;
|
||||
|
||||
ret = dvb_register_adapter(&dvb->dvb_adapter,
|
||||
CX18_DRIVER_NAME,
|
||||
THIS_MODULE, &cx->dev->dev, adapter_nr);
|
||||
if (ret < 0)
|
||||
goto err_out;
|
||||
|
||||
dvb_adapter = &dvb->dvb_adapter;
|
||||
|
||||
dvbdemux = &dvb->demux;
|
||||
|
||||
dvbdemux->priv = (void *)stream;
|
||||
|
||||
dvbdemux->filternum = 256;
|
||||
dvbdemux->feednum = 256;
|
||||
dvbdemux->start_feed = cx18_dvb_start_feed;
|
||||
dvbdemux->stop_feed = cx18_dvb_stop_feed;
|
||||
dvbdemux->dmx.capabilities = (DMX_TS_FILTERING |
|
||||
DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
|
||||
ret = dvb_dmx_init(dvbdemux);
|
||||
if (ret < 0)
|
||||
goto err_dvb_unregister_adapter;
|
||||
|
||||
dmx = &dvbdemux->dmx;
|
||||
|
||||
dvb->hw_frontend.source = DMX_FRONTEND_0;
|
||||
dvb->mem_frontend.source = DMX_MEMORY_FE;
|
||||
dvb->dmxdev.filternum = 256;
|
||||
dvb->dmxdev.demux = dmx;
|
||||
|
||||
ret = dvb_dmxdev_init(&dvb->dmxdev, dvb_adapter);
|
||||
if (ret < 0)
|
||||
goto err_dvb_dmx_release;
|
||||
|
||||
ret = dmx->add_frontend(dmx, &dvb->hw_frontend);
|
||||
if (ret < 0)
|
||||
goto err_dvb_dmxdev_release;
|
||||
|
||||
ret = dmx->add_frontend(dmx, &dvb->mem_frontend);
|
||||
if (ret < 0)
|
||||
goto err_remove_hw_frontend;
|
||||
|
||||
ret = dmx->connect_frontend(dmx, &dvb->hw_frontend);
|
||||
if (ret < 0)
|
||||
goto err_remove_mem_frontend;
|
||||
|
||||
ret = dvb_register(stream);
|
||||
if (ret < 0)
|
||||
goto err_disconnect_frontend;
|
||||
|
||||
dvb_net_init(dvb_adapter, &dvb->dvbnet, dmx);
|
||||
|
||||
CX18_INFO("DVB Frontend registered\n");
|
||||
mutex_init(&dvb->feedlock);
|
||||
dvb->enabled = 1;
|
||||
return ret;
|
||||
|
||||
err_disconnect_frontend:
|
||||
dmx->disconnect_frontend(dmx);
|
||||
err_remove_mem_frontend:
|
||||
dmx->remove_frontend(dmx, &dvb->mem_frontend);
|
||||
err_remove_hw_frontend:
|
||||
dmx->remove_frontend(dmx, &dvb->hw_frontend);
|
||||
err_dvb_dmxdev_release:
|
||||
dvb_dmxdev_release(&dvb->dmxdev);
|
||||
err_dvb_dmx_release:
|
||||
dvb_dmx_release(dvbdemux);
|
||||
err_dvb_unregister_adapter:
|
||||
dvb_unregister_adapter(dvb_adapter);
|
||||
err_out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
void cx18_dvb_unregister(struct cx18_stream *stream)
|
||||
{
|
||||
struct cx18 *cx = stream->cx;
|
||||
struct cx18_dvb *dvb = &stream->dvb;
|
||||
struct dvb_adapter *dvb_adapter;
|
||||
struct dvb_demux *dvbdemux;
|
||||
struct dmx_demux *dmx;
|
||||
|
||||
CX18_INFO("unregister DVB\n");
|
||||
|
||||
dvb_adapter = &dvb->dvb_adapter;
|
||||
dvbdemux = &dvb->demux;
|
||||
dmx = &dvbdemux->dmx;
|
||||
|
||||
dmx->close(dmx);
|
||||
dvb_net_release(&dvb->dvbnet);
|
||||
dmx->remove_frontend(dmx, &dvb->mem_frontend);
|
||||
dmx->remove_frontend(dmx, &dvb->hw_frontend);
|
||||
dvb_dmxdev_release(&dvb->dmxdev);
|
||||
dvb_dmx_release(dvbdemux);
|
||||
dvb_unregister_frontend(dvb->fe);
|
||||
dvb_frontend_detach(dvb->fe);
|
||||
dvb_unregister_adapter(dvb_adapter);
|
||||
}
|
||||
|
||||
/* All the DVB attach calls go here, this function get's modified
|
||||
* for each new card. No other function in this file needs
|
||||
* to change.
|
||||
*/
|
||||
static int dvb_register(struct cx18_stream *stream)
|
||||
{
|
||||
struct cx18_dvb *dvb = &stream->dvb;
|
||||
struct cx18 *cx = stream->cx;
|
||||
int ret = 0;
|
||||
|
||||
switch (cx->card->type) {
|
||||
/* Wait until the MXL500X driver is merged */
|
||||
#ifdef HAVE_MXL500X
|
||||
case CX18_CARD_HVR_1600_ESMT:
|
||||
case CX18_CARD_HVR_1600_SAMSUNG:
|
||||
dvb->fe = dvb_attach(s5h1409_attach,
|
||||
&hauppauge_hvr1600_config,
|
||||
&cx->i2c_adap[0]);
|
||||
if (dvb->fe != NULL) {
|
||||
dvb_attach(mxl500x_attach, dvb->fe,
|
||||
&hauppauge_hvr1600_tuner,
|
||||
&cx->i2c_adap[0]);
|
||||
ret = 0;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
/* No Digital Tv Support */
|
||||
break;
|
||||
}
|
||||
|
||||
if (dvb->fe == NULL) {
|
||||
CX18_ERR("frontend initialization failed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = dvb_register_frontend(&dvb->dvb_adapter, dvb->fe);
|
||||
if (ret < 0) {
|
||||
if (dvb->fe->ops.release)
|
||||
dvb->fe->ops.release(dvb->fe);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* cx18 functions for DVB support
|
||||
*
|
||||
* Copyright (c) 2008 Steven Toth <stoth@hauppauge.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 "cx18-driver.h"
|
||||
|
||||
int cx18_dvb_register(struct cx18_stream *stream);
|
||||
void cx18_dvb_unregister(struct cx18_stream *stream);
|
|
@ -0,0 +1,711 @@
|
|||
/*
|
||||
* cx18 file operation functions
|
||||
*
|
||||
* Derived from ivtv-fileops.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-fileops.h"
|
||||
#include "cx18-i2c.h"
|
||||
#include "cx18-queue.h"
|
||||
#include "cx18-vbi.h"
|
||||
#include "cx18-audio.h"
|
||||
#include "cx18-mailbox.h"
|
||||
#include "cx18-scb.h"
|
||||
#include "cx18-streams.h"
|
||||
#include "cx18-controls.h"
|
||||
#include "cx18-ioctl.h"
|
||||
#include "cx18-cards.h"
|
||||
|
||||
/* This function tries to claim the stream for a specific file descriptor.
|
||||
If no one else is using this stream then the stream is claimed and
|
||||
associated VBI streams are also automatically claimed.
|
||||
Possible error returns: -EBUSY if someone else has claimed
|
||||
the stream or 0 on success. */
|
||||
int cx18_claim_stream(struct cx18_open_id *id, int type)
|
||||
{
|
||||
struct cx18 *cx = id->cx;
|
||||
struct cx18_stream *s = &cx->streams[type];
|
||||
struct cx18_stream *s_vbi;
|
||||
int vbi_type;
|
||||
|
||||
if (test_and_set_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
|
||||
/* someone already claimed this stream */
|
||||
if (s->id == id->open_id) {
|
||||
/* yes, this file descriptor did. So that's OK. */
|
||||
return 0;
|
||||
}
|
||||
if (s->id == -1 && type == CX18_ENC_STREAM_TYPE_VBI) {
|
||||
/* VBI is handled already internally, now also assign
|
||||
the file descriptor to this stream for external
|
||||
reading of the stream. */
|
||||
s->id = id->open_id;
|
||||
CX18_DEBUG_INFO("Start Read VBI\n");
|
||||
return 0;
|
||||
}
|
||||
/* someone else is using this stream already */
|
||||
CX18_DEBUG_INFO("Stream %d is busy\n", type);
|
||||
return -EBUSY;
|
||||
}
|
||||
s->id = id->open_id;
|
||||
|
||||
/* CX18_DEC_STREAM_TYPE_MPG needs to claim CX18_DEC_STREAM_TYPE_VBI,
|
||||
CX18_ENC_STREAM_TYPE_MPG needs to claim CX18_ENC_STREAM_TYPE_VBI
|
||||
(provided VBI insertion is on and sliced VBI is selected), for all
|
||||
other streams we're done */
|
||||
if (type == CX18_ENC_STREAM_TYPE_MPG &&
|
||||
cx->vbi.insert_mpeg && cx->vbi.sliced_in->service_set) {
|
||||
vbi_type = CX18_ENC_STREAM_TYPE_VBI;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
s_vbi = &cx->streams[vbi_type];
|
||||
|
||||
set_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
|
||||
|
||||
/* mark that it is used internally */
|
||||
set_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* This function releases a previously claimed stream. It will take into
|
||||
account associated VBI streams. */
|
||||
void cx18_release_stream(struct cx18_stream *s)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
struct cx18_stream *s_vbi;
|
||||
|
||||
s->id = -1;
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
|
||||
test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags)) {
|
||||
/* this stream is still in use internally */
|
||||
return;
|
||||
}
|
||||
if (!test_and_clear_bit(CX18_F_S_CLAIMED, &s->s_flags)) {
|
||||
CX18_DEBUG_WARN("Release stream %s not in use!\n", s->name);
|
||||
return;
|
||||
}
|
||||
|
||||
cx18_flush_queues(s);
|
||||
|
||||
/* CX18_ENC_STREAM_TYPE_MPG needs to release CX18_ENC_STREAM_TYPE_VBI,
|
||||
for all other streams we're done */
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG)
|
||||
s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
|
||||
else
|
||||
return;
|
||||
|
||||
/* clear internal use flag */
|
||||
if (!test_and_clear_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags)) {
|
||||
/* was already cleared */
|
||||
return;
|
||||
}
|
||||
if (s_vbi->id != -1) {
|
||||
/* VBI stream still claimed by a file descriptor */
|
||||
return;
|
||||
}
|
||||
clear_bit(CX18_F_S_CLAIMED, &s_vbi->s_flags);
|
||||
cx18_flush_queues(s_vbi);
|
||||
}
|
||||
|
||||
static void cx18_dualwatch(struct cx18 *cx)
|
||||
{
|
||||
struct v4l2_tuner vt;
|
||||
u16 new_bitmap;
|
||||
u16 new_stereo_mode;
|
||||
const u16 stereo_mask = 0x0300;
|
||||
const u16 dual = 0x0200;
|
||||
|
||||
new_stereo_mode = cx->params.audio_properties & stereo_mask;
|
||||
memset(&vt, 0, sizeof(vt));
|
||||
cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);
|
||||
if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
|
||||
(vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
|
||||
new_stereo_mode = dual;
|
||||
|
||||
if (new_stereo_mode == cx->dualwatch_stereo_mode)
|
||||
return;
|
||||
|
||||
new_bitmap = new_stereo_mode | (cx->params.audio_properties & ~stereo_mask);
|
||||
|
||||
CX18_DEBUG_INFO("dualwatch: change stereo flag from 0x%x to 0x%x. new audio_bitmask=0x%ux\n",
|
||||
cx->dualwatch_stereo_mode, new_stereo_mode, new_bitmap);
|
||||
|
||||
if (cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
|
||||
cx18_find_handle(cx), new_bitmap) == 0) {
|
||||
cx->dualwatch_stereo_mode = new_stereo_mode;
|
||||
return;
|
||||
}
|
||||
CX18_DEBUG_INFO("dualwatch: changing stereo flag failed\n");
|
||||
}
|
||||
|
||||
|
||||
static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block, int *err)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
struct cx18_stream *s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
|
||||
struct cx18_buffer *buf;
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
*err = 0;
|
||||
while (1) {
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
|
||||
|
||||
if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
|
||||
cx->dualwatch_jiffies = jiffies;
|
||||
cx18_dualwatch(cx);
|
||||
}
|
||||
if (test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
|
||||
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
|
||||
while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
|
||||
/* byteswap and process VBI data */
|
||||
/* cx18_process_vbi_data(cx, buf, s_vbi->dma_pts, s_vbi->type); */
|
||||
cx18_enqueue(s_vbi, buf, &s_vbi->q_free);
|
||||
}
|
||||
}
|
||||
buf = &cx->vbi.sliced_mpeg_buf;
|
||||
if (buf->readpos != buf->bytesused)
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* do we have leftover data? */
|
||||
buf = cx18_dequeue(s, &s->q_io);
|
||||
if (buf)
|
||||
return buf;
|
||||
|
||||
/* do we have new data? */
|
||||
buf = cx18_dequeue(s, &s->q_full);
|
||||
if (buf) {
|
||||
if (!test_and_clear_bit(CX18_F_B_NEED_BUF_SWAP,
|
||||
&buf->b_flags))
|
||||
return buf;
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG)
|
||||
/* byteswap MPG data */
|
||||
cx18_buf_swap(buf);
|
||||
else {
|
||||
/* byteswap and process VBI data */
|
||||
cx18_process_vbi_data(cx, buf,
|
||||
s->dma_pts, s->type);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* return if end of stream */
|
||||
if (!test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
|
||||
CX18_DEBUG_INFO("EOS %s\n", s->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* return if file was opened with O_NONBLOCK */
|
||||
if (non_block) {
|
||||
*err = -EAGAIN;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* wait for more data to arrive */
|
||||
prepare_to_wait(&s->waitq, &wait, TASK_INTERRUPTIBLE);
|
||||
/* New buffers might have become available before we were added
|
||||
to the waitqueue */
|
||||
if (!s->q_full.buffers)
|
||||
schedule();
|
||||
finish_wait(&s->waitq, &wait);
|
||||
if (signal_pending(current)) {
|
||||
/* return if a signal was received */
|
||||
CX18_DEBUG_INFO("User stopped %s\n", s->name);
|
||||
*err = -EINTR;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cx18_setup_sliced_vbi_buf(struct cx18 *cx)
|
||||
{
|
||||
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
|
||||
|
||||
cx->vbi.sliced_mpeg_buf.buf = cx->vbi.sliced_mpeg_data[idx];
|
||||
cx->vbi.sliced_mpeg_buf.bytesused = cx->vbi.sliced_mpeg_size[idx];
|
||||
cx->vbi.sliced_mpeg_buf.readpos = 0;
|
||||
}
|
||||
|
||||
static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
|
||||
struct cx18_buffer *buf, char __user *ubuf, size_t ucount)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
size_t len = buf->bytesused - buf->readpos;
|
||||
|
||||
if (len > ucount)
|
||||
len = ucount;
|
||||
if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
|
||||
cx->vbi.sliced_in->service_set && buf != &cx->vbi.sliced_mpeg_buf) {
|
||||
const char *start = buf->buf + buf->readpos;
|
||||
const char *p = start + 1;
|
||||
const u8 *q;
|
||||
u8 ch = cx->search_pack_header ? 0xba : 0xe0;
|
||||
int stuffing, i;
|
||||
|
||||
while (start + len > p) {
|
||||
q = memchr(p, 0, start + len - p);
|
||||
if (q == NULL)
|
||||
break;
|
||||
p = q + 1;
|
||||
if ((char *)q + 15 >= buf->buf + buf->bytesused ||
|
||||
q[1] != 0 || q[2] != 1 || q[3] != ch)
|
||||
continue;
|
||||
if (!cx->search_pack_header) {
|
||||
if ((q[6] & 0xc0) != 0x80)
|
||||
continue;
|
||||
if (((q[7] & 0xc0) == 0x80 &&
|
||||
(q[9] & 0xf0) == 0x20) ||
|
||||
((q[7] & 0xc0) == 0xc0 &&
|
||||
(q[9] & 0xf0) == 0x30)) {
|
||||
ch = 0xba;
|
||||
cx->search_pack_header = 1;
|
||||
p = q + 9;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
stuffing = q[13] & 7;
|
||||
/* all stuffing bytes must be 0xff */
|
||||
for (i = 0; i < stuffing; i++)
|
||||
if (q[14 + i] != 0xff)
|
||||
break;
|
||||
if (i == stuffing &&
|
||||
(q[4] & 0xc4) == 0x44 &&
|
||||
(q[12] & 3) == 3 &&
|
||||
q[14 + stuffing] == 0 &&
|
||||
q[15 + stuffing] == 0 &&
|
||||
q[16 + stuffing] == 1) {
|
||||
cx->search_pack_header = 0;
|
||||
len = (char *)q - start;
|
||||
cx18_setup_sliced_vbi_buf(cx);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (copy_to_user(ubuf, (u8 *)buf->buf + buf->readpos, len)) {
|
||||
CX18_DEBUG_WARN("copy %zd bytes to user failed for %s\n",
|
||||
len, s->name);
|
||||
return -EFAULT;
|
||||
}
|
||||
buf->readpos += len;
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
|
||||
buf != &cx->vbi.sliced_mpeg_buf)
|
||||
cx->mpg_data_received += len;
|
||||
return len;
|
||||
}
|
||||
|
||||
static ssize_t cx18_read(struct cx18_stream *s, char __user *ubuf,
|
||||
size_t tot_count, int non_block)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
size_t tot_written = 0;
|
||||
int single_frame = 0;
|
||||
|
||||
if (atomic_read(&cx->capturing) == 0 && s->id == -1) {
|
||||
/* shouldn't happen */
|
||||
CX18_DEBUG_WARN("Stream %s not initialized before read\n",
|
||||
s->name);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Each VBI buffer is one frame, the v4l2 API says that for VBI the
|
||||
frames should arrive one-by-one, so make sure we never output more
|
||||
than one VBI frame at a time */
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_VBI &&
|
||||
cx->vbi.sliced_in->service_set)
|
||||
single_frame = 1;
|
||||
|
||||
for (;;) {
|
||||
struct cx18_buffer *buf;
|
||||
int rc;
|
||||
|
||||
buf = cx18_get_buffer(s, non_block, &rc);
|
||||
/* if there is no data available... */
|
||||
if (buf == NULL) {
|
||||
/* if we got data, then return that regardless */
|
||||
if (tot_written)
|
||||
break;
|
||||
/* EOS condition */
|
||||
if (rc == 0) {
|
||||
clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
|
||||
clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
|
||||
cx18_release_stream(s);
|
||||
}
|
||||
/* set errno */
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = cx18_copy_buf_to_user(s, buf, ubuf + tot_written,
|
||||
tot_count - tot_written);
|
||||
|
||||
if (buf != &cx->vbi.sliced_mpeg_buf) {
|
||||
if (buf->readpos == buf->bytesused) {
|
||||
cx18_buf_sync_for_device(s, buf);
|
||||
cx18_enqueue(s, buf, &s->q_free);
|
||||
cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5,
|
||||
s->handle,
|
||||
(void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
|
||||
1, buf->id, s->buf_size);
|
||||
} else
|
||||
cx18_enqueue(s, buf, &s->q_io);
|
||||
} else if (buf->readpos == buf->bytesused) {
|
||||
int idx = cx->vbi.inserted_frame % CX18_VBI_FRAMES;
|
||||
|
||||
cx->vbi.sliced_mpeg_size[idx] = 0;
|
||||
cx->vbi.inserted_frame++;
|
||||
cx->vbi_data_inserted += buf->bytesused;
|
||||
}
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
tot_written += rc;
|
||||
|
||||
if (tot_written == tot_count || single_frame)
|
||||
break;
|
||||
}
|
||||
return tot_written;
|
||||
}
|
||||
|
||||
static ssize_t cx18_read_pos(struct cx18_stream *s, char __user *ubuf,
|
||||
size_t count, loff_t *pos, int non_block)
|
||||
{
|
||||
ssize_t rc = count ? cx18_read(s, ubuf, count, non_block) : 0;
|
||||
struct cx18 *cx = s->cx;
|
||||
|
||||
CX18_DEBUG_HI_FILE("read %zd from %s, got %zd\n", count, s->name, rc);
|
||||
if (rc > 0)
|
||||
pos += rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
int cx18_start_capture(struct cx18_open_id *id)
|
||||
{
|
||||
struct cx18 *cx = id->cx;
|
||||
struct cx18_stream *s = &cx->streams[id->type];
|
||||
struct cx18_stream *s_vbi;
|
||||
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_RAD) {
|
||||
/* you cannot read from these stream types. */
|
||||
return -EPERM;
|
||||
}
|
||||
|
||||
/* Try to claim this stream. */
|
||||
if (cx18_claim_stream(id, s->type))
|
||||
return -EBUSY;
|
||||
|
||||
/* If capture is already in progress, then we also have to
|
||||
do nothing extra. */
|
||||
if (test_bit(CX18_F_S_STREAMOFF, &s->s_flags) ||
|
||||
test_and_set_bit(CX18_F_S_STREAMING, &s->s_flags)) {
|
||||
set_bit(CX18_F_S_APPL_IO, &s->s_flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Start VBI capture if required */
|
||||
s_vbi = &cx->streams[CX18_ENC_STREAM_TYPE_VBI];
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
|
||||
test_bit(CX18_F_S_INTERNAL_USE, &s_vbi->s_flags) &&
|
||||
!test_and_set_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
|
||||
/* Note: the CX18_ENC_STREAM_TYPE_VBI is claimed
|
||||
automatically when the MPG stream is claimed.
|
||||
We only need to start the VBI capturing. */
|
||||
if (cx18_start_v4l2_encode_stream(s_vbi)) {
|
||||
CX18_DEBUG_WARN("VBI capture start failed\n");
|
||||
|
||||
/* Failure, clean up and return an error */
|
||||
clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
|
||||
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
|
||||
/* also releases the associated VBI stream */
|
||||
cx18_release_stream(s);
|
||||
return -EIO;
|
||||
}
|
||||
CX18_DEBUG_INFO("VBI insertion started\n");
|
||||
}
|
||||
|
||||
/* Tell the card to start capturing */
|
||||
if (!cx18_start_v4l2_encode_stream(s)) {
|
||||
/* We're done */
|
||||
set_bit(CX18_F_S_APPL_IO, &s->s_flags);
|
||||
/* Resume a possibly paused encoder */
|
||||
if (test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
|
||||
cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, s->handle);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* failure, clean up */
|
||||
CX18_DEBUG_WARN("Failed to start capturing for stream %s\n", s->name);
|
||||
|
||||
/* Note: the CX18_ENC_STREAM_TYPE_VBI is released
|
||||
automatically when the MPG stream is released.
|
||||
We only need to stop the VBI capturing. */
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG &&
|
||||
test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags)) {
|
||||
cx18_stop_v4l2_encode_stream(s_vbi, 0);
|
||||
clear_bit(CX18_F_S_STREAMING, &s_vbi->s_flags);
|
||||
}
|
||||
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
|
||||
cx18_release_stream(s);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
|
||||
loff_t *pos)
|
||||
{
|
||||
struct cx18_open_id *id = filp->private_data;
|
||||
struct cx18 *cx = id->cx;
|
||||
struct cx18_stream *s = &cx->streams[id->type];
|
||||
int rc;
|
||||
|
||||
CX18_DEBUG_HI_FILE("read %zd bytes from %s\n", count, s->name);
|
||||
|
||||
mutex_lock(&cx->serialize_lock);
|
||||
rc = cx18_start_capture(id);
|
||||
mutex_unlock(&cx->serialize_lock);
|
||||
if (rc)
|
||||
return rc;
|
||||
return cx18_read_pos(s, buf, count, pos, filp->f_flags & O_NONBLOCK);
|
||||
}
|
||||
|
||||
unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait)
|
||||
{
|
||||
struct cx18_open_id *id = filp->private_data;
|
||||
struct cx18 *cx = id->cx;
|
||||
struct cx18_stream *s = &cx->streams[id->type];
|
||||
int eof = test_bit(CX18_F_S_STREAMOFF, &s->s_flags);
|
||||
|
||||
/* Start a capture if there is none */
|
||||
if (!eof && !test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
|
||||
int rc;
|
||||
|
||||
mutex_lock(&cx->serialize_lock);
|
||||
rc = cx18_start_capture(id);
|
||||
mutex_unlock(&cx->serialize_lock);
|
||||
if (rc) {
|
||||
CX18_DEBUG_INFO("Could not start capture for %s (%d)\n",
|
||||
s->name, rc);
|
||||
return POLLERR;
|
||||
}
|
||||
CX18_DEBUG_FILE("Encoder poll started capture\n");
|
||||
}
|
||||
|
||||
/* add stream's waitq to the poll list */
|
||||
CX18_DEBUG_HI_FILE("Encoder poll\n");
|
||||
poll_wait(filp, &s->waitq, wait);
|
||||
|
||||
if (s->q_full.length || s->q_io.length)
|
||||
return POLLIN | POLLRDNORM;
|
||||
if (eof)
|
||||
return POLLHUP;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cx18_stop_capture(struct cx18_open_id *id, int gop_end)
|
||||
{
|
||||
struct cx18 *cx = id->cx;
|
||||
struct cx18_stream *s = &cx->streams[id->type];
|
||||
|
||||
CX18_DEBUG_IOCTL("close() of %s\n", s->name);
|
||||
|
||||
/* 'Unclaim' this stream */
|
||||
|
||||
/* Stop capturing */
|
||||
if (test_bit(CX18_F_S_STREAMING, &s->s_flags)) {
|
||||
struct cx18_stream *s_vbi =
|
||||
&cx->streams[CX18_ENC_STREAM_TYPE_VBI];
|
||||
|
||||
CX18_DEBUG_INFO("close stopping capture\n");
|
||||
/* Special case: a running VBI capture for VBI insertion
|
||||
in the mpeg stream. Need to stop that too. */
|
||||
if (id->type == CX18_ENC_STREAM_TYPE_MPG &&
|
||||
test_bit(CX18_F_S_STREAMING, &s_vbi->s_flags) &&
|
||||
!test_bit(CX18_F_S_APPL_IO, &s_vbi->s_flags)) {
|
||||
CX18_DEBUG_INFO("close stopping embedded VBI capture\n");
|
||||
cx18_stop_v4l2_encode_stream(s_vbi, 0);
|
||||
}
|
||||
if (id->type == CX18_ENC_STREAM_TYPE_VBI &&
|
||||
test_bit(CX18_F_S_INTERNAL_USE, &s->s_flags))
|
||||
/* Also used internally, don't stop capturing */
|
||||
s->id = -1;
|
||||
else
|
||||
cx18_stop_v4l2_encode_stream(s, gop_end);
|
||||
}
|
||||
if (!gop_end) {
|
||||
clear_bit(CX18_F_S_APPL_IO, &s->s_flags);
|
||||
clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
|
||||
cx18_release_stream(s);
|
||||
}
|
||||
}
|
||||
|
||||
int cx18_v4l2_close(struct inode *inode, struct file *filp)
|
||||
{
|
||||
struct cx18_open_id *id = filp->private_data;
|
||||
struct cx18 *cx = id->cx;
|
||||
struct cx18_stream *s = &cx->streams[id->type];
|
||||
|
||||
CX18_DEBUG_IOCTL("close() of %s\n", s->name);
|
||||
|
||||
v4l2_prio_close(&cx->prio, &id->prio);
|
||||
|
||||
/* Easy case first: this stream was never claimed by us */
|
||||
if (s->id != id->open_id) {
|
||||
kfree(id);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 'Unclaim' this stream */
|
||||
|
||||
/* Stop radio */
|
||||
mutex_lock(&cx->serialize_lock);
|
||||
if (id->type == CX18_ENC_STREAM_TYPE_RAD) {
|
||||
/* Closing radio device, return to TV mode */
|
||||
cx18_mute(cx);
|
||||
/* Mark that the radio is no longer in use */
|
||||
clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
|
||||
/* Switch tuner to TV */
|
||||
cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
|
||||
/* Select correct audio input (i.e. TV tuner or Line in) */
|
||||
cx18_audio_set_io(cx);
|
||||
if (atomic_read(&cx->capturing) > 0) {
|
||||
/* Undo video mute */
|
||||
cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
|
||||
cx->params.video_mute |
|
||||
(cx->params.video_mute_yuv << 8));
|
||||
}
|
||||
/* Done! Unmute and continue. */
|
||||
cx18_unmute(cx);
|
||||
cx18_release_stream(s);
|
||||
} else {
|
||||
cx18_stop_capture(id, 0);
|
||||
}
|
||||
kfree(id);
|
||||
mutex_unlock(&cx->serialize_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
struct cx18_open_id *item;
|
||||
|
||||
CX18_DEBUG_FILE("open %s\n", s->name);
|
||||
|
||||
/* Allocate memory */
|
||||
item = kmalloc(sizeof(struct cx18_open_id), GFP_KERNEL);
|
||||
if (NULL == item) {
|
||||
CX18_DEBUG_WARN("nomem on v4l2 open\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
item->cx = cx;
|
||||
item->type = s->type;
|
||||
v4l2_prio_open(&cx->prio, &item->prio);
|
||||
|
||||
item->open_id = cx->open_id++;
|
||||
filp->private_data = item;
|
||||
|
||||
if (item->type == CX18_ENC_STREAM_TYPE_RAD) {
|
||||
/* Try to claim this stream */
|
||||
if (cx18_claim_stream(item, item->type)) {
|
||||
/* No, it's already in use */
|
||||
kfree(item);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
|
||||
if (atomic_read(&cx->capturing) > 0) {
|
||||
/* switching to radio while capture is
|
||||
in progress is not polite */
|
||||
cx18_release_stream(s);
|
||||
kfree(item);
|
||||
return -EBUSY;
|
||||
}
|
||||
}
|
||||
|
||||
/* Mark that the radio is being used. */
|
||||
set_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
|
||||
/* We have the radio */
|
||||
cx18_mute(cx);
|
||||
/* Switch tuner to radio */
|
||||
cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
|
||||
/* Select the correct audio input (i.e. radio tuner) */
|
||||
cx18_audio_set_io(cx);
|
||||
/* Done! Unmute and continue. */
|
||||
cx18_unmute(cx);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx18_v4l2_open(struct inode *inode, struct file *filp)
|
||||
{
|
||||
int res, x, y = 0;
|
||||
struct cx18 *cx = NULL;
|
||||
struct cx18_stream *s = NULL;
|
||||
int minor = iminor(inode);
|
||||
|
||||
/* Find which card this open was on */
|
||||
spin_lock(&cx18_cards_lock);
|
||||
for (x = 0; cx == NULL && x < cx18_cards_active; x++) {
|
||||
/* find out which stream this open was on */
|
||||
for (y = 0; y < CX18_MAX_STREAMS; y++) {
|
||||
s = &cx18_cards[x]->streams[y];
|
||||
if (s->v4l2dev && s->v4l2dev->minor == minor) {
|
||||
cx = cx18_cards[x];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock(&cx18_cards_lock);
|
||||
|
||||
if (cx == NULL) {
|
||||
/* Couldn't find a device registered
|
||||
on that minor, shouldn't happen! */
|
||||
printk(KERN_WARNING "No cx18 device found on minor %d\n",
|
||||
minor);
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
mutex_lock(&cx->serialize_lock);
|
||||
if (cx18_init_on_first_open(cx)) {
|
||||
CX18_ERR("Failed to initialize on minor %d\n", minor);
|
||||
mutex_unlock(&cx->serialize_lock);
|
||||
return -ENXIO;
|
||||
}
|
||||
res = cx18_serialized_open(s, filp);
|
||||
mutex_unlock(&cx->serialize_lock);
|
||||
return res;
|
||||
}
|
||||
|
||||
void cx18_mute(struct cx18 *cx)
|
||||
{
|
||||
if (atomic_read(&cx->capturing))
|
||||
cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
|
||||
cx18_find_handle(cx), 1);
|
||||
CX18_DEBUG_INFO("Mute\n");
|
||||
}
|
||||
|
||||
void cx18_unmute(struct cx18 *cx)
|
||||
{
|
||||
if (atomic_read(&cx->capturing)) {
|
||||
cx18_msleep_timeout(100, 0);
|
||||
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
|
||||
cx18_find_handle(cx), 12);
|
||||
cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
|
||||
cx18_find_handle(cx), 0);
|
||||
}
|
||||
CX18_DEBUG_INFO("Unmute\n");
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/*
|
||||
* cx18 file operation functions
|
||||
*
|
||||
* Derived from ivtv-fileops.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
/* Testing/Debugging */
|
||||
int cx18_v4l2_open(struct inode *inode, struct file *filp);
|
||||
ssize_t cx18_v4l2_read(struct file *filp, char __user *buf, size_t count,
|
||||
loff_t *pos);
|
||||
ssize_t cx18_v4l2_write(struct file *filp, const char __user *buf, size_t count,
|
||||
loff_t *pos);
|
||||
int cx18_v4l2_close(struct inode *inode, struct file *filp);
|
||||
unsigned int cx18_v4l2_enc_poll(struct file *filp, poll_table *wait);
|
||||
int cx18_start_capture(struct cx18_open_id *id);
|
||||
void cx18_stop_capture(struct cx18_open_id *id, int gop_end);
|
||||
void cx18_mute(struct cx18 *cx);
|
||||
void cx18_unmute(struct cx18 *cx);
|
||||
|
||||
/* Utilities */
|
||||
|
||||
/* Try to claim a stream for the filehandle. Return 0 on success,
|
||||
-EBUSY if stream already claimed. Once a stream is claimed, it
|
||||
remains claimed until the associated filehandle is closed. */
|
||||
int cx18_claim_stream(struct cx18_open_id *id, int type);
|
||||
|
||||
/* Release a previously claimed stream. */
|
||||
void cx18_release_stream(struct cx18_stream *s);
|
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
* cx18 firmware functions
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-scb.h"
|
||||
#include "cx18-irq.h"
|
||||
#include "cx18-firmware.h"
|
||||
#include "cx18-cards.h"
|
||||
#include <linux/firmware.h>
|
||||
|
||||
#define CX18_PROC_SOFT_RESET 0xc70010
|
||||
#define CX18_DDR_SOFT_RESET 0xc70014
|
||||
#define CX18_CLOCK_SELECT1 0xc71000
|
||||
#define CX18_CLOCK_SELECT2 0xc71004
|
||||
#define CX18_HALF_CLOCK_SELECT1 0xc71008
|
||||
#define CX18_HALF_CLOCK_SELECT2 0xc7100C
|
||||
#define CX18_CLOCK_POLARITY1 0xc71010
|
||||
#define CX18_CLOCK_POLARITY2 0xc71014
|
||||
#define CX18_ADD_DELAY_ENABLE1 0xc71018
|
||||
#define CX18_ADD_DELAY_ENABLE2 0xc7101C
|
||||
#define CX18_CLOCK_ENABLE1 0xc71020
|
||||
#define CX18_CLOCK_ENABLE2 0xc71024
|
||||
|
||||
#define CX18_REG_BUS_TIMEOUT_EN 0xc72024
|
||||
|
||||
#define CX18_AUDIO_ENABLE 0xc72014
|
||||
#define CX18_REG_BUS_TIMEOUT_EN 0xc72024
|
||||
|
||||
#define CX18_FAST_CLOCK_PLL_INT 0xc78000
|
||||
#define CX18_FAST_CLOCK_PLL_FRAC 0xc78004
|
||||
#define CX18_FAST_CLOCK_PLL_POST 0xc78008
|
||||
#define CX18_FAST_CLOCK_PLL_PRESCALE 0xc7800C
|
||||
#define CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH 0xc78010
|
||||
|
||||
#define CX18_SLOW_CLOCK_PLL_INT 0xc78014
|
||||
#define CX18_SLOW_CLOCK_PLL_FRAC 0xc78018
|
||||
#define CX18_SLOW_CLOCK_PLL_POST 0xc7801C
|
||||
#define CX18_MPEG_CLOCK_PLL_INT 0xc78040
|
||||
#define CX18_MPEG_CLOCK_PLL_FRAC 0xc78044
|
||||
#define CX18_MPEG_CLOCK_PLL_POST 0xc78048
|
||||
#define CX18_PLL_POWER_DOWN 0xc78088
|
||||
#define CX18_SW1_INT_STATUS 0xc73104
|
||||
#define CX18_SW1_INT_ENABLE_PCI 0xc7311C
|
||||
#define CX18_SW2_INT_SET 0xc73140
|
||||
#define CX18_SW2_INT_STATUS 0xc73144
|
||||
#define CX18_ADEC_CONTROL 0xc78120
|
||||
|
||||
#define CX18_DDR_REQUEST_ENABLE 0xc80000
|
||||
#define CX18_DDR_CHIP_CONFIG 0xc80004
|
||||
#define CX18_DDR_REFRESH 0xc80008
|
||||
#define CX18_DDR_TIMING1 0xc8000C
|
||||
#define CX18_DDR_TIMING2 0xc80010
|
||||
#define CX18_DDR_POWER_REG 0xc8001C
|
||||
|
||||
#define CX18_DDR_TUNE_LANE 0xc80048
|
||||
#define CX18_DDR_INITIAL_EMRS 0xc80054
|
||||
#define CX18_DDR_MB_PER_ROW_7 0xc8009C
|
||||
#define CX18_DDR_BASE_63_ADDR 0xc804FC
|
||||
|
||||
#define CX18_WMB_CLIENT02 0xc90108
|
||||
#define CX18_WMB_CLIENT05 0xc90114
|
||||
#define CX18_WMB_CLIENT06 0xc90118
|
||||
#define CX18_WMB_CLIENT07 0xc9011C
|
||||
#define CX18_WMB_CLIENT08 0xc90120
|
||||
#define CX18_WMB_CLIENT09 0xc90124
|
||||
#define CX18_WMB_CLIENT10 0xc90128
|
||||
#define CX18_WMB_CLIENT11 0xc9012C
|
||||
#define CX18_WMB_CLIENT12 0xc90130
|
||||
#define CX18_WMB_CLIENT13 0xc90134
|
||||
#define CX18_WMB_CLIENT14 0xc90138
|
||||
|
||||
#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
|
||||
|
||||
/* Encoder/decoder firmware sizes */
|
||||
#define CX18_FW_CPU_SIZE (174716)
|
||||
#define CX18_FW_APU_SIZE (141200)
|
||||
|
||||
#define APU_ROM_SYNC1 0x6D676553 /* "mgeS" */
|
||||
#define APU_ROM_SYNC2 0x72646548 /* "rdeH" */
|
||||
|
||||
struct cx18_apu_rom_seghdr {
|
||||
u32 sync1;
|
||||
u32 sync2;
|
||||
u32 addr;
|
||||
u32 size;
|
||||
};
|
||||
|
||||
static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx, long size)
|
||||
{
|
||||
const struct firmware *fw = NULL;
|
||||
int retries = 3;
|
||||
int i, j;
|
||||
u32 __iomem *dst = (u32 __iomem *)mem;
|
||||
const u32 *src;
|
||||
|
||||
retry:
|
||||
if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) {
|
||||
CX18_ERR("Unable to open firmware %s (must be %ld bytes)\n",
|
||||
fn, size);
|
||||
CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
src = (const u32 *)fw->data;
|
||||
|
||||
if (fw->size != size) {
|
||||
/* Due to race conditions in firmware loading (esp. with
|
||||
udev <0.95) the wrong file was sometimes loaded. So we check
|
||||
filesizes to see if at least the right-sized file was
|
||||
loaded. If not, then we retry. */
|
||||
CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n",
|
||||
fn, size, fw->size);
|
||||
release_firmware(fw);
|
||||
retries--;
|
||||
goto retry;
|
||||
}
|
||||
for (i = 0; i < fw->size; i += 4096) {
|
||||
setup_page(i);
|
||||
for (j = i; j < fw->size && j < i + 4096; j += 4) {
|
||||
/* no need for endianness conversion on the ppc */
|
||||
__raw_writel(*src, dst);
|
||||
if (__raw_readl(dst) != *src) {
|
||||
CX18_ERR("Mismatch at offset %x\n", i);
|
||||
release_firmware(fw);
|
||||
return -EIO;
|
||||
}
|
||||
dst++;
|
||||
src++;
|
||||
}
|
||||
}
|
||||
if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
|
||||
CX18_INFO("loaded %s firmware (%zd bytes)\n", fn, fw->size);
|
||||
release_firmware(fw);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx, long size)
|
||||
{
|
||||
const struct firmware *fw = NULL;
|
||||
int retries = 3;
|
||||
int i, j;
|
||||
const u32 *src;
|
||||
struct cx18_apu_rom_seghdr seghdr;
|
||||
const u8 *vers;
|
||||
u32 offset = 0;
|
||||
u32 apu_version = 0;
|
||||
int sz;
|
||||
|
||||
retry:
|
||||
if (!retries || request_firmware(&fw, fn, &cx->dev->dev)) {
|
||||
CX18_ERR("unable to open firmware %s (must be %ld bytes)\n",
|
||||
fn, size);
|
||||
CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
src = (const u32 *)fw->data;
|
||||
vers = fw->data + sizeof(seghdr);
|
||||
sz = fw->size;
|
||||
|
||||
if (fw->size != size) {
|
||||
/* Due to race conditions in firmware loading (esp. with
|
||||
udev <0.95) the wrong file was sometimes loaded. So we check
|
||||
filesizes to see if at least the right-sized file was
|
||||
loaded. If not, then we retry. */
|
||||
CX18_INFO("retry: file loaded was not %s (expected size %ld, got %zd)\n",
|
||||
fn, size, fw->size);
|
||||
release_firmware(fw);
|
||||
retries--;
|
||||
goto retry;
|
||||
}
|
||||
apu_version = (vers[0] << 24) | (vers[4] << 16) | vers[32];
|
||||
while (offset + sizeof(seghdr) < size) {
|
||||
/* TODO: byteswapping */
|
||||
memcpy(&seghdr, src + offset / 4, sizeof(seghdr));
|
||||
offset += sizeof(seghdr);
|
||||
if (seghdr.sync1 != APU_ROM_SYNC1 ||
|
||||
seghdr.sync2 != APU_ROM_SYNC2) {
|
||||
offset += seghdr.size;
|
||||
continue;
|
||||
}
|
||||
CX18_DEBUG_INFO("load segment %x-%x\n", seghdr.addr,
|
||||
seghdr.addr + seghdr.size - 1);
|
||||
if (offset + seghdr.size > sz)
|
||||
break;
|
||||
for (i = 0; i < seghdr.size; i += 4096) {
|
||||
setup_page(offset + i);
|
||||
for (j = i; j < seghdr.size && j < i + 4096; j += 4) {
|
||||
/* no need for endianness conversion on the ppc */
|
||||
__raw_writel(src[(offset + j) / 4], dst + seghdr.addr + j);
|
||||
if (__raw_readl(dst + seghdr.addr + j) != src[(offset + j) / 4]) {
|
||||
CX18_ERR("Mismatch at offset %x\n", offset + j);
|
||||
release_firmware(fw);
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
}
|
||||
offset += seghdr.size;
|
||||
}
|
||||
if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags))
|
||||
CX18_INFO("loaded %s firmware V%08x (%zd bytes)\n",
|
||||
fn, apu_version, fw->size);
|
||||
release_firmware(fw);
|
||||
/* Clear bit0 for APU to start from 0 */
|
||||
write_reg(read_reg(0xc72030) & ~1, 0xc72030);
|
||||
return size;
|
||||
}
|
||||
|
||||
void cx18_halt_firmware(struct cx18 *cx)
|
||||
{
|
||||
CX18_DEBUG_INFO("Preparing for firmware halt.\n");
|
||||
write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
|
||||
write_reg(0x00020002, CX18_ADEC_CONTROL);
|
||||
}
|
||||
|
||||
void cx18_init_power(struct cx18 *cx, int lowpwr)
|
||||
{
|
||||
/* power-down Spare and AOM PLLs */
|
||||
/* power-up fast, slow and mpeg PLLs */
|
||||
write_reg(0x00000008, CX18_PLL_POWER_DOWN);
|
||||
|
||||
/* ADEC out of sleep */
|
||||
write_reg(0x00020000, CX18_ADEC_CONTROL);
|
||||
|
||||
/* The fast clock is at 200/245 MHz */
|
||||
write_reg(lowpwr ? 0xD : 0x11, CX18_FAST_CLOCK_PLL_INT);
|
||||
write_reg(lowpwr ? 0x1EFBF37 : 0x038E3D7, CX18_FAST_CLOCK_PLL_FRAC);
|
||||
|
||||
write_reg(2, CX18_FAST_CLOCK_PLL_POST);
|
||||
write_reg(1, CX18_FAST_CLOCK_PLL_PRESCALE);
|
||||
write_reg(4, CX18_FAST_CLOCK_PLL_ADJUST_BANDWIDTH);
|
||||
|
||||
/* set slow clock to 125/120 MHz */
|
||||
write_reg(lowpwr ? 0x11 : 0x10, CX18_SLOW_CLOCK_PLL_INT);
|
||||
write_reg(lowpwr ? 0xEBAF05 : 0x18618A8, CX18_SLOW_CLOCK_PLL_FRAC);
|
||||
write_reg(4, CX18_SLOW_CLOCK_PLL_POST);
|
||||
|
||||
/* mpeg clock pll 54MHz */
|
||||
write_reg(0xF, CX18_MPEG_CLOCK_PLL_INT);
|
||||
write_reg(0x2BCFEF, CX18_MPEG_CLOCK_PLL_FRAC);
|
||||
write_reg(8, CX18_MPEG_CLOCK_PLL_POST);
|
||||
|
||||
/* Defaults */
|
||||
/* APU = SC or SC/2 = 125/62.5 */
|
||||
/* EPU = SC = 125 */
|
||||
/* DDR = FC = 180 */
|
||||
/* ENC = SC = 125 */
|
||||
/* AI1 = SC = 125 */
|
||||
/* VIM2 = disabled */
|
||||
/* PCI = FC/2 = 90 */
|
||||
/* AI2 = disabled */
|
||||
/* DEMUX = disabled */
|
||||
/* AO = SC/2 = 62.5 */
|
||||
/* SER = 54MHz */
|
||||
/* VFC = disabled */
|
||||
/* USB = disabled */
|
||||
|
||||
write_reg(lowpwr ? 0xFFFF0020 : 0x00060004, CX18_CLOCK_SELECT1);
|
||||
write_reg(lowpwr ? 0xFFFF0004 : 0x00060006, CX18_CLOCK_SELECT2);
|
||||
|
||||
write_reg(0xFFFF0002, CX18_HALF_CLOCK_SELECT1);
|
||||
write_reg(0xFFFF0104, CX18_HALF_CLOCK_SELECT2);
|
||||
|
||||
write_reg(0xFFFF9026, CX18_CLOCK_ENABLE1);
|
||||
write_reg(0xFFFF3105, CX18_CLOCK_ENABLE2);
|
||||
}
|
||||
|
||||
void cx18_init_memory(struct cx18 *cx)
|
||||
{
|
||||
cx18_msleep_timeout(10, 0);
|
||||
write_reg(0x10000, CX18_DDR_SOFT_RESET);
|
||||
cx18_msleep_timeout(10, 0);
|
||||
|
||||
write_reg(cx->card->ddr.chip_config, CX18_DDR_CHIP_CONFIG);
|
||||
|
||||
cx18_msleep_timeout(10, 0);
|
||||
|
||||
write_reg(cx->card->ddr.refresh, CX18_DDR_REFRESH);
|
||||
write_reg(cx->card->ddr.timing1, CX18_DDR_TIMING1);
|
||||
write_reg(cx->card->ddr.timing2, CX18_DDR_TIMING2);
|
||||
|
||||
cx18_msleep_timeout(10, 0);
|
||||
|
||||
/* Initialize DQS pad time */
|
||||
write_reg(cx->card->ddr.tune_lane, CX18_DDR_TUNE_LANE);
|
||||
write_reg(cx->card->ddr.initial_emrs, CX18_DDR_INITIAL_EMRS);
|
||||
|
||||
cx18_msleep_timeout(10, 0);
|
||||
|
||||
write_reg(0x20000, CX18_DDR_SOFT_RESET);
|
||||
cx18_msleep_timeout(10, 0);
|
||||
|
||||
/* use power-down mode when idle */
|
||||
write_reg(0x00000010, CX18_DDR_POWER_REG);
|
||||
|
||||
write_reg(0x10001, CX18_REG_BUS_TIMEOUT_EN);
|
||||
|
||||
write_reg(0x48, CX18_DDR_MB_PER_ROW_7);
|
||||
write_reg(0xE0000, CX18_DDR_BASE_63_ADDR);
|
||||
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT02); /* AO */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT09); /* AI2 */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT05); /* VIM1 */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT06); /* AI1 */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT07); /* 3D comb */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT10); /* ME */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT12); /* ENC */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT13); /* PK */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT11); /* RC */
|
||||
write_reg(0x00000101, CX18_WMB_CLIENT14); /* AVO */
|
||||
}
|
||||
|
||||
int cx18_firmware_init(struct cx18 *cx)
|
||||
{
|
||||
/* Allow chip to control CLKRUN */
|
||||
write_reg(0x5, CX18_DSP0_INTERRUPT_MASK);
|
||||
|
||||
write_reg(0x000F000F, CX18_PROC_SOFT_RESET); /* stop the fw */
|
||||
|
||||
cx18_msleep_timeout(1, 0);
|
||||
|
||||
sw1_irq_enable(IRQ_CPU_TO_EPU | IRQ_APU_TO_EPU);
|
||||
sw2_irq_enable(IRQ_CPU_TO_EPU_ACK | IRQ_APU_TO_EPU_ACK);
|
||||
|
||||
/* Only if the processor is not running */
|
||||
if (read_reg(CX18_PROC_SOFT_RESET) & 8) {
|
||||
int sz = load_apu_fw_direct("v4l-cx23418-apu.fw",
|
||||
cx->enc_mem, cx, CX18_FW_APU_SIZE);
|
||||
|
||||
sz = sz <= 0 ? sz : load_cpu_fw_direct("v4l-cx23418-cpu.fw",
|
||||
cx->enc_mem, cx, CX18_FW_CPU_SIZE);
|
||||
|
||||
if (sz > 0) {
|
||||
int retries = 0;
|
||||
|
||||
/* start the CPU */
|
||||
write_reg(0x00080000, CX18_PROC_SOFT_RESET);
|
||||
while (retries++ < 50) { /* Loop for max 500mS */
|
||||
if ((read_reg(CX18_PROC_SOFT_RESET) & 1) == 0)
|
||||
break;
|
||||
cx18_msleep_timeout(10, 0);
|
||||
}
|
||||
cx18_msleep_timeout(200, 0);
|
||||
if (retries == 51) {
|
||||
CX18_ERR("Could not start the CPU\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
if (sz <= 0)
|
||||
return -EIO;
|
||||
}
|
||||
/* initialize GPIO */
|
||||
write_reg(0x14001400, 0xC78110);
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* cx18 firmware functions
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
int cx18_firmware_init(struct cx18 *cx);
|
||||
void cx18_halt_firmware(struct cx18 *cx);
|
||||
void cx18_init_memory(struct cx18 *cx);
|
||||
void cx18_init_power(struct cx18 *cx, int lowpwr);
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* cx18 gpio functions
|
||||
*
|
||||
* Derived from ivtv-gpio.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-gpio.h"
|
||||
#include "tuner-xc2028.h"
|
||||
|
||||
/********************* GPIO stuffs *********************/
|
||||
|
||||
/* GPIO registers */
|
||||
#define CX18_REG_GPIO_IN 0xc72010
|
||||
#define CX18_REG_GPIO_OUT1 0xc78100
|
||||
#define CX18_REG_GPIO_DIR1 0xc78108
|
||||
#define CX18_REG_GPIO_OUT2 0xc78104
|
||||
#define CX18_REG_GPIO_DIR2 0xc7810c
|
||||
|
||||
/*
|
||||
* HVR-1600 GPIO pins, courtesy of Hauppauge:
|
||||
*
|
||||
* gpio0: zilog ir process reset pin
|
||||
* gpio1: zilog programming pin (you should never use this)
|
||||
* gpio12: cx24227 reset pin
|
||||
* gpio13: cs5345 reset pin
|
||||
*/
|
||||
|
||||
void cx18_gpio_init(struct cx18 *cx)
|
||||
{
|
||||
if (cx->card->gpio_init.direction == 0)
|
||||
return;
|
||||
|
||||
CX18_DEBUG_INFO("GPIO initial dir: %08x out: %08x\n",
|
||||
read_reg(CX18_REG_GPIO_DIR1), read_reg(CX18_REG_GPIO_OUT1));
|
||||
|
||||
/* init output data then direction */
|
||||
write_reg(cx->card->gpio_init.direction << 16, CX18_REG_GPIO_DIR1);
|
||||
write_reg(0, CX18_REG_GPIO_DIR2);
|
||||
write_reg((cx->card->gpio_init.direction << 16) |
|
||||
cx->card->gpio_init.initial_value, CX18_REG_GPIO_OUT1);
|
||||
write_reg(0, CX18_REG_GPIO_OUT2);
|
||||
}
|
||||
|
||||
/* Xceive tuner reset function */
|
||||
int cx18_reset_tuner_gpio(void *dev, int cmd, int value)
|
||||
{
|
||||
struct i2c_algo_bit_data *algo = dev;
|
||||
struct cx18 *cx = algo->data;
|
||||
/* int curdir, curout;*/
|
||||
|
||||
if (cmd != XC2028_TUNER_RESET)
|
||||
return 0;
|
||||
CX18_DEBUG_INFO("Resetting tuner\n");
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* cx18 gpio functions
|
||||
*
|
||||
* Derived from ivtv-gpio.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
void cx18_gpio_init(struct cx18 *cx);
|
||||
int cx18_reset_tuner_gpio(void *dev, int cmd, int value);
|
|
@ -0,0 +1,431 @@
|
|||
/*
|
||||
* cx18 I2C functions
|
||||
*
|
||||
* Derived from ivtv-i2c.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-gpio.h"
|
||||
#include "cx18-av-core.h"
|
||||
|
||||
#include <media/ir-kbd-i2c.h>
|
||||
|
||||
#define CX18_REG_I2C_1_WR 0xf15000
|
||||
#define CX18_REG_I2C_1_RD 0xf15008
|
||||
#define CX18_REG_I2C_2_WR 0xf25100
|
||||
#define CX18_REG_I2C_2_RD 0xf25108
|
||||
|
||||
#define SETSCL_BIT 0x0001
|
||||
#define SETSDL_BIT 0x0002
|
||||
#define GETSCL_BIT 0x0004
|
||||
#define GETSDL_BIT 0x0008
|
||||
|
||||
#ifndef I2C_ADAP_CLASS_TV_ANALOG
|
||||
#define I2C_ADAP_CLASS_TV_ANALOG I2C_CLASS_TV_ANALOG
|
||||
#endif
|
||||
|
||||
#define CX18_CS5345_I2C_ADDR 0x4c
|
||||
|
||||
/* This array should match the CX18_HW_ defines */
|
||||
static const u8 hw_driverids[] = {
|
||||
I2C_DRIVERID_TUNER,
|
||||
I2C_DRIVERID_TVEEPROM,
|
||||
I2C_DRIVERID_CS5345,
|
||||
0, /* CX18_HW_GPIO dummy driver ID */
|
||||
0 /* CX18_HW_CX23418 dummy driver ID */
|
||||
};
|
||||
|
||||
/* This array should match the CX18_HW_ defines */
|
||||
static const u8 hw_addrs[] = {
|
||||
0,
|
||||
0,
|
||||
CX18_CS5345_I2C_ADDR,
|
||||
0, /* CX18_HW_GPIO dummy driver ID */
|
||||
0, /* CX18_HW_CX23418 dummy driver ID */
|
||||
};
|
||||
|
||||
/* This array should match the CX18_HW_ defines */
|
||||
/* This might well become a card-specific array */
|
||||
static const u8 hw_bus[] = {
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0, /* CX18_HW_GPIO dummy driver ID */
|
||||
0, /* CX18_HW_CX23418 dummy driver ID */
|
||||
};
|
||||
|
||||
/* This array should match the CX18_HW_ defines */
|
||||
static const char * const hw_drivernames[] = {
|
||||
"tuner",
|
||||
"tveeprom",
|
||||
"cs5345",
|
||||
"gpio",
|
||||
"cx23418",
|
||||
};
|
||||
|
||||
int cx18_i2c_register(struct cx18 *cx, unsigned idx)
|
||||
{
|
||||
struct i2c_board_info info;
|
||||
struct i2c_client *c;
|
||||
u8 id, bus;
|
||||
int i;
|
||||
|
||||
CX18_DEBUG_I2C("i2c client register\n");
|
||||
if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
|
||||
return -1;
|
||||
id = hw_driverids[idx];
|
||||
bus = hw_bus[idx];
|
||||
memset(&info, 0, sizeof(info));
|
||||
strlcpy(info.driver_name, hw_drivernames[idx],
|
||||
sizeof(info.driver_name));
|
||||
info.addr = hw_addrs[idx];
|
||||
for (i = 0; i < I2C_CLIENTS_MAX; i++)
|
||||
if (cx->i2c_clients[i] == NULL)
|
||||
break;
|
||||
|
||||
if (i == I2C_CLIENTS_MAX) {
|
||||
CX18_ERR("insufficient room for new I2C client!\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (id != I2C_DRIVERID_TUNER) {
|
||||
c = i2c_new_device(&cx->i2c_adap[bus], &info);
|
||||
if (c->driver == NULL)
|
||||
i2c_unregister_device(c);
|
||||
else
|
||||
cx->i2c_clients[i] = c;
|
||||
return cx->i2c_clients[i] ? 0 : -ENODEV;
|
||||
}
|
||||
|
||||
/* special tuner handling */
|
||||
c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
|
||||
if (c && c->driver == NULL)
|
||||
i2c_unregister_device(c);
|
||||
else if (c)
|
||||
cx->i2c_clients[i++] = c;
|
||||
c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
|
||||
if (c && c->driver == NULL)
|
||||
i2c_unregister_device(c);
|
||||
else if (c)
|
||||
cx->i2c_clients[i++] = c;
|
||||
c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
|
||||
if (c && c->driver == NULL)
|
||||
i2c_unregister_device(c);
|
||||
else if (c)
|
||||
cx->i2c_clients[i++] = c;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int attach_inform(struct i2c_client *client)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int detach_inform(struct i2c_client *client)
|
||||
{
|
||||
int i;
|
||||
struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
|
||||
|
||||
CX18_DEBUG_I2C("i2c client detach\n");
|
||||
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
|
||||
if (cx->i2c_clients[i] == client) {
|
||||
cx->i2c_clients[i] = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
|
||||
client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cx18_setscl(void *data, int state)
|
||||
{
|
||||
struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
|
||||
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
|
||||
u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
|
||||
u32 r = read_reg(addr);
|
||||
|
||||
if (state)
|
||||
write_reg_sync(r | SETSCL_BIT, addr);
|
||||
else
|
||||
write_reg_sync(r & ~SETSCL_BIT, addr);
|
||||
}
|
||||
|
||||
static void cx18_setsda(void *data, int state)
|
||||
{
|
||||
struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
|
||||
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
|
||||
u32 addr = bus_index ? CX18_REG_I2C_2_WR : CX18_REG_I2C_1_WR;
|
||||
u32 r = read_reg(addr);
|
||||
|
||||
if (state)
|
||||
write_reg_sync(r | SETSDL_BIT, addr);
|
||||
else
|
||||
write_reg_sync(r & ~SETSDL_BIT, addr);
|
||||
}
|
||||
|
||||
static int cx18_getscl(void *data)
|
||||
{
|
||||
struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
|
||||
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
|
||||
u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
|
||||
|
||||
return read_reg(addr) & GETSCL_BIT;
|
||||
}
|
||||
|
||||
static int cx18_getsda(void *data)
|
||||
{
|
||||
struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
|
||||
int bus_index = ((struct cx18_i2c_algo_callback_data *)data)->bus_index;
|
||||
u32 addr = bus_index ? CX18_REG_I2C_2_RD : CX18_REG_I2C_1_RD;
|
||||
|
||||
return read_reg(addr) & GETSDL_BIT;
|
||||
}
|
||||
|
||||
/* template for i2c-bit-algo */
|
||||
static struct i2c_adapter cx18_i2c_adap_template = {
|
||||
.name = "cx18 i2c driver",
|
||||
.id = I2C_HW_B_CX2341X,
|
||||
.algo = NULL, /* set by i2c-algo-bit */
|
||||
.algo_data = NULL, /* filled from template */
|
||||
.client_register = attach_inform,
|
||||
.client_unregister = detach_inform,
|
||||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
#define CX18_SCL_PERIOD (10) /* usecs. 10 usec is period for a 100 KHz clock */
|
||||
#define CX18_ALGO_BIT_TIMEOUT (2) /* seconds */
|
||||
|
||||
static struct i2c_algo_bit_data cx18_i2c_algo_template = {
|
||||
.setsda = cx18_setsda,
|
||||
.setscl = cx18_setscl,
|
||||
.getsda = cx18_getsda,
|
||||
.getscl = cx18_getscl,
|
||||
.udelay = CX18_SCL_PERIOD/2, /* 1/2 clock period in usec*/
|
||||
.timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
|
||||
};
|
||||
|
||||
static struct i2c_client cx18_i2c_client_template = {
|
||||
.name = "cx18 internal",
|
||||
};
|
||||
|
||||
int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
int retval;
|
||||
int i;
|
||||
|
||||
CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
|
||||
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
|
||||
client = cx->i2c_clients[i];
|
||||
if (client == NULL || client->driver == NULL ||
|
||||
client->driver->command == NULL)
|
||||
continue;
|
||||
if (addr == client->addr) {
|
||||
retval = client->driver->command(client, cmd, arg);
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
if (cmd != VIDIOC_G_CHIP_IDENT)
|
||||
CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",
|
||||
addr, cmd);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Find the i2c device based on the driver ID and return
|
||||
its i2c address or -ENODEV if no matching device was found. */
|
||||
static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
|
||||
{
|
||||
struct i2c_client *client;
|
||||
int retval = -ENODEV;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < I2C_CLIENTS_MAX; i++) {
|
||||
client = cx->i2c_clients[i];
|
||||
if (client == NULL || client->driver == NULL)
|
||||
continue;
|
||||
if (id == client->driver->id) {
|
||||
retval = client->addr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
/* Find the i2c device name matching the DRIVERID */
|
||||
static const char *cx18_i2c_id_name(u32 id)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
|
||||
if (hw_driverids[i] == id)
|
||||
return hw_drivernames[i];
|
||||
return "unknown device";
|
||||
}
|
||||
|
||||
/* Find the i2c device name matching the CX18_HW_ flag */
|
||||
static const char *cx18_i2c_hw_name(u32 hw)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
|
||||
if (1 << i == hw)
|
||||
return hw_drivernames[i];
|
||||
return "unknown device";
|
||||
}
|
||||
|
||||
/* Find the i2c device matching the CX18_HW_ flag and return
|
||||
its i2c address or -ENODEV if no matching device was found. */
|
||||
int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
|
||||
if (1 << i == hw)
|
||||
return cx18_i2c_id_addr(cx, hw_driverids[i]);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
|
||||
If hw == CX18_HW_GPIO then call the gpio handler. */
|
||||
int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
|
||||
{
|
||||
int addr;
|
||||
|
||||
if (hw == CX18_HW_GPIO || hw == 0)
|
||||
return 0;
|
||||
if (hw == CX18_HW_CX23418)
|
||||
return cx18_av_cmd(cx, cmd, arg);
|
||||
|
||||
addr = cx18_i2c_hw_addr(cx, hw);
|
||||
if (addr < 0) {
|
||||
CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
|
||||
hw, cx18_i2c_hw_name(hw), cmd);
|
||||
return addr;
|
||||
}
|
||||
return cx18_call_i2c_client(cx, addr, cmd, arg);
|
||||
}
|
||||
|
||||
/* Calls i2c device based on I2C driver ID. */
|
||||
int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg)
|
||||
{
|
||||
int addr;
|
||||
|
||||
addr = cx18_i2c_id_addr(cx, id);
|
||||
if (addr < 0) {
|
||||
if (cmd != VIDIOC_G_CHIP_IDENT)
|
||||
CX18_ERR("i2c ID 0x%08x (%s) not found for cmd 0x%x!\n",
|
||||
id, cx18_i2c_id_name(id), cmd);
|
||||
return addr;
|
||||
}
|
||||
return cx18_call_i2c_client(cx, addr, cmd, arg);
|
||||
}
|
||||
|
||||
/* broadcast cmd for all I2C clients and for the gpio subsystem */
|
||||
void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
|
||||
{
|
||||
if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
|
||||
CX18_ERR("adapter is not set\n");
|
||||
return;
|
||||
}
|
||||
cx18_av_cmd(cx, cmd, arg);
|
||||
i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
|
||||
i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
|
||||
}
|
||||
|
||||
/* init + register i2c algo-bit adapter */
|
||||
int init_cx18_i2c(struct cx18 *cx)
|
||||
{
|
||||
int i;
|
||||
CX18_DEBUG_I2C("i2c init\n");
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
|
||||
sizeof(struct i2c_adapter));
|
||||
memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
|
||||
sizeof(struct i2c_algo_bit_data));
|
||||
cx->i2c_algo_cb_data[i].cx = cx;
|
||||
cx->i2c_algo_cb_data[i].bus_index = i;
|
||||
cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
|
||||
cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
|
||||
|
||||
sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
|
||||
" #%d-%d", cx->num, i);
|
||||
i2c_set_adapdata(&cx->i2c_adap[i], cx);
|
||||
|
||||
memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
|
||||
sizeof(struct i2c_client));
|
||||
sprintf(cx->i2c_client[i].name +
|
||||
strlen(cx->i2c_client[i].name), "%d", i);
|
||||
cx->i2c_client[i].adapter = &cx->i2c_adap[i];
|
||||
cx->i2c_adap[i].dev.parent = &cx->dev->dev;
|
||||
}
|
||||
|
||||
if (read_reg(CX18_REG_I2C_2_WR) != 0x0003c02f) {
|
||||
/* Reset/Unreset I2C hardware block */
|
||||
write_reg(0x10000000, 0xc71004); /* Clock select 220MHz */
|
||||
write_reg_sync(0x10001000, 0xc71024); /* Clock Enable */
|
||||
}
|
||||
/* courtesy of Steven Toth <stoth@hauppauge.com> */
|
||||
write_reg_sync(0x00c00000, 0xc7001c);
|
||||
mdelay(10);
|
||||
write_reg_sync(0x00c000c0, 0xc7001c);
|
||||
mdelay(10);
|
||||
write_reg_sync(0x00c00000, 0xc7001c);
|
||||
|
||||
write_reg_sync(0x00c00000, 0xc730c8); /* Set to edge-triggered intrs. */
|
||||
write_reg_sync(0x00c00000, 0xc730c4); /* Clear any stale intrs */
|
||||
|
||||
/* Hw I2C1 Clock Freq ~100kHz */
|
||||
write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_1_WR);
|
||||
cx18_setscl(&cx->i2c_algo_cb_data[0], 1);
|
||||
cx18_setsda(&cx->i2c_algo_cb_data[0], 1);
|
||||
|
||||
/* Hw I2C2 Clock Freq ~100kHz */
|
||||
write_reg_sync(0x00021c0f & ~4, CX18_REG_I2C_2_WR);
|
||||
cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
|
||||
cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
|
||||
|
||||
return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
|
||||
i2c_bit_add_bus(&cx->i2c_adap[1]);
|
||||
}
|
||||
|
||||
void exit_cx18_i2c(struct cx18 *cx)
|
||||
{
|
||||
int i;
|
||||
CX18_DEBUG_I2C("i2c exit\n");
|
||||
write_reg(read_reg(CX18_REG_I2C_1_WR) | 4, CX18_REG_I2C_1_WR);
|
||||
write_reg(read_reg(CX18_REG_I2C_2_WR) | 4, CX18_REG_I2C_2_WR);
|
||||
|
||||
for (i = 0; i < 2; i++) {
|
||||
i2c_del_adapter(&cx->i2c_adap[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Hauppauge HVR1600 should have:
|
||||
32 cx24227
|
||||
98 unknown
|
||||
a0 eeprom
|
||||
c2 tuner
|
||||
e? zilog ir
|
||||
*/
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* cx18 I2C functions
|
||||
*
|
||||
* Derived from ivtv-i2c.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
|
||||
int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
|
||||
int cx18_i2c_id(struct cx18 *cx, u32 id, unsigned int cmd, void *arg);
|
||||
int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
|
||||
void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
|
||||
int cx18_i2c_register(struct cx18 *cx, unsigned idx);
|
||||
|
||||
/* init + register i2c algo-bit adapter */
|
||||
int init_cx18_i2c(struct cx18 *cx);
|
||||
void exit_cx18_i2c(struct cx18 *cx);
|
|
@ -0,0 +1,851 @@
|
|||
/*
|
||||
* cx18 ioctl system call
|
||||
*
|
||||
* Derived from ivtv-ioctl.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-version.h"
|
||||
#include "cx18-mailbox.h"
|
||||
#include "cx18-i2c.h"
|
||||
#include "cx18-queue.h"
|
||||
#include "cx18-fileops.h"
|
||||
#include "cx18-vbi.h"
|
||||
#include "cx18-audio.h"
|
||||
#include "cx18-video.h"
|
||||
#include "cx18-streams.h"
|
||||
#include "cx18-ioctl.h"
|
||||
#include "cx18-gpio.h"
|
||||
#include "cx18-controls.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-av-core.h"
|
||||
#include <media/tveeprom.h>
|
||||
#include <media/v4l2-chip-ident.h>
|
||||
#include <linux/i2c-id.h>
|
||||
|
||||
u16 cx18_service2vbi(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case V4L2_SLICED_TELETEXT_B:
|
||||
return CX18_SLICED_TYPE_TELETEXT_B;
|
||||
case V4L2_SLICED_CAPTION_525:
|
||||
return CX18_SLICED_TYPE_CAPTION_525;
|
||||
case V4L2_SLICED_WSS_625:
|
||||
return CX18_SLICED_TYPE_WSS_625;
|
||||
case V4L2_SLICED_VPS:
|
||||
return CX18_SLICED_TYPE_VPS;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int valid_service_line(int field, int line, int is_pal)
|
||||
{
|
||||
return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
|
||||
(!is_pal && line >= 10 && line < 22);
|
||||
}
|
||||
|
||||
static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
|
||||
{
|
||||
u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
|
||||
int i;
|
||||
|
||||
set = set & valid_set;
|
||||
if (set == 0 || !valid_service_line(field, line, is_pal))
|
||||
return 0;
|
||||
if (!is_pal) {
|
||||
if (line == 21 && (set & V4L2_SLICED_CAPTION_525))
|
||||
return V4L2_SLICED_CAPTION_525;
|
||||
} else {
|
||||
if (line == 16 && field == 0 && (set & V4L2_SLICED_VPS))
|
||||
return V4L2_SLICED_VPS;
|
||||
if (line == 23 && field == 0 && (set & V4L2_SLICED_WSS_625))
|
||||
return V4L2_SLICED_WSS_625;
|
||||
if (line == 23)
|
||||
return 0;
|
||||
}
|
||||
for (i = 0; i < 32; i++) {
|
||||
if ((1 << i) & set)
|
||||
return 1 << i;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
|
||||
{
|
||||
u16 set = fmt->service_set;
|
||||
int f, l;
|
||||
|
||||
fmt->service_set = 0;
|
||||
for (f = 0; f < 2; f++) {
|
||||
for (l = 0; l < 24; l++)
|
||||
fmt->service_lines[f][l] = select_service_from_set(f, l, set, is_pal);
|
||||
}
|
||||
}
|
||||
|
||||
static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
|
||||
{
|
||||
int f, l;
|
||||
u16 set = 0;
|
||||
|
||||
for (f = 0; f < 2; f++) {
|
||||
for (l = 0; l < 24; l++) {
|
||||
fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
|
||||
set |= fmt->service_lines[f][l];
|
||||
}
|
||||
}
|
||||
return set != 0;
|
||||
}
|
||||
|
||||
u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
|
||||
{
|
||||
int f, l;
|
||||
u16 set = 0;
|
||||
|
||||
for (f = 0; f < 2; f++) {
|
||||
for (l = 0; l < 24; l++)
|
||||
set |= fmt->service_lines[f][l];
|
||||
}
|
||||
return set;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
v4l2_std_id std;
|
||||
char *name;
|
||||
} enum_stds[] = {
|
||||
{ V4L2_STD_PAL_BG | V4L2_STD_PAL_H, "PAL-BGH" },
|
||||
{ V4L2_STD_PAL_DK, "PAL-DK" },
|
||||
{ V4L2_STD_PAL_I, "PAL-I" },
|
||||
{ V4L2_STD_PAL_M, "PAL-M" },
|
||||
{ V4L2_STD_PAL_N, "PAL-N" },
|
||||
{ V4L2_STD_PAL_Nc, "PAL-Nc" },
|
||||
{ V4L2_STD_SECAM_B | V4L2_STD_SECAM_G | V4L2_STD_SECAM_H, "SECAM-BGH" },
|
||||
{ V4L2_STD_SECAM_DK, "SECAM-DK" },
|
||||
{ V4L2_STD_SECAM_L, "SECAM-L" },
|
||||
{ V4L2_STD_SECAM_LC, "SECAM-L'" },
|
||||
{ V4L2_STD_NTSC_M, "NTSC-M" },
|
||||
{ V4L2_STD_NTSC_M_JP, "NTSC-J" },
|
||||
{ V4L2_STD_NTSC_M_KR, "NTSC-K" },
|
||||
};
|
||||
|
||||
static const struct v4l2_standard cx18_std_60hz = {
|
||||
.frameperiod = {.numerator = 1001, .denominator = 30000},
|
||||
.framelines = 525,
|
||||
};
|
||||
|
||||
static const struct v4l2_standard cx18_std_50hz = {
|
||||
.frameperiod = { .numerator = 1, .denominator = 25 },
|
||||
.framelines = 625,
|
||||
};
|
||||
|
||||
static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct v4l2_register *regs = arg;
|
||||
unsigned long flags;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
|
||||
return -EINVAL;
|
||||
|
||||
spin_lock_irqsave(&cx18_cards_lock, flags);
|
||||
if (cmd == VIDIOC_DBG_G_REGISTER)
|
||||
regs->val = read_enc(regs->reg);
|
||||
else
|
||||
write_enc(regs->val, regs->reg);
|
||||
spin_unlock_irqrestore(&cx18_cards_lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_get_fmt(struct cx18 *cx, int streamtype, struct v4l2_format *fmt)
|
||||
{
|
||||
switch (fmt->type) {
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
||||
fmt->fmt.pix.width = cx->params.width;
|
||||
fmt->fmt.pix.height = cx->params.height;
|
||||
fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
|
||||
fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
|
||||
if (streamtype == CX18_ENC_STREAM_TYPE_YUV) {
|
||||
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_HM12;
|
||||
/* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
|
||||
fmt->fmt.pix.sizeimage =
|
||||
fmt->fmt.pix.height * fmt->fmt.pix.width +
|
||||
fmt->fmt.pix.height * (fmt->fmt.pix.width / 2);
|
||||
} else {
|
||||
fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
|
||||
fmt->fmt.pix.sizeimage = 128 * 1024;
|
||||
}
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_VBI_CAPTURE:
|
||||
fmt->fmt.vbi.sampling_rate = 27000000;
|
||||
fmt->fmt.vbi.offset = 248;
|
||||
fmt->fmt.vbi.samples_per_line = cx->vbi.raw_decoder_line_size - 4;
|
||||
fmt->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
|
||||
fmt->fmt.vbi.start[0] = cx->vbi.start[0];
|
||||
fmt->fmt.vbi.start[1] = cx->vbi.start[1];
|
||||
fmt->fmt.vbi.count[0] = fmt->fmt.vbi.count[1] = cx->vbi.count;
|
||||
break;
|
||||
|
||||
case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
|
||||
{
|
||||
struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
|
||||
|
||||
vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
|
||||
memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
|
||||
memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
|
||||
|
||||
cx18_av_cmd(cx, VIDIOC_G_FMT, fmt);
|
||||
vbifmt->service_set = cx18_get_service_set(vbifmt);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_try_or_set_fmt(struct cx18 *cx, int streamtype,
|
||||
struct v4l2_format *fmt, int set_fmt)
|
||||
{
|
||||
struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
|
||||
u16 set;
|
||||
|
||||
/* set window size */
|
||||
if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
||||
int w = fmt->fmt.pix.width;
|
||||
int h = fmt->fmt.pix.height;
|
||||
|
||||
if (w > 720)
|
||||
w = 720;
|
||||
else if (w < 1)
|
||||
w = 1;
|
||||
if (h > (cx->is_50hz ? 576 : 480))
|
||||
h = (cx->is_50hz ? 576 : 480);
|
||||
else if (h < 2)
|
||||
h = 2;
|
||||
cx18_get_fmt(cx, streamtype, fmt);
|
||||
fmt->fmt.pix.width = w;
|
||||
fmt->fmt.pix.height = h;
|
||||
|
||||
if (!set_fmt || (cx->params.width == w && cx->params.height == h))
|
||||
return 0;
|
||||
if (atomic_read(&cx->capturing) > 0)
|
||||
return -EBUSY;
|
||||
|
||||
cx->params.width = w;
|
||||
cx->params.height = h;
|
||||
if (w != 720 || h != (cx->is_50hz ? 576 : 480))
|
||||
cx->params.video_temporal_filter = 0;
|
||||
else
|
||||
cx->params.video_temporal_filter = 8;
|
||||
cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
|
||||
return cx18_get_fmt(cx, streamtype, fmt);
|
||||
}
|
||||
|
||||
/* set raw VBI format */
|
||||
if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
|
||||
if (set_fmt && streamtype == CX18_ENC_STREAM_TYPE_VBI &&
|
||||
cx->vbi.sliced_in->service_set &&
|
||||
atomic_read(&cx->capturing) > 0)
|
||||
return -EBUSY;
|
||||
if (set_fmt) {
|
||||
cx->vbi.sliced_in->service_set = 0;
|
||||
cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
|
||||
}
|
||||
return cx18_get_fmt(cx, streamtype, fmt);
|
||||
}
|
||||
|
||||
/* any else but sliced VBI capture is an error */
|
||||
if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
|
||||
return -EINVAL;
|
||||
|
||||
/* TODO: implement sliced VBI, for now silently return 0 */
|
||||
return 0;
|
||||
|
||||
/* set sliced VBI capture format */
|
||||
vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
|
||||
memset(vbifmt->reserved, 0, sizeof(vbifmt->reserved));
|
||||
|
||||
if (vbifmt->service_set)
|
||||
cx18_expand_service_set(vbifmt, cx->is_50hz);
|
||||
set = check_service_set(vbifmt, cx->is_50hz);
|
||||
vbifmt->service_set = cx18_get_service_set(vbifmt);
|
||||
|
||||
if (!set_fmt)
|
||||
return 0;
|
||||
if (set == 0)
|
||||
return -EINVAL;
|
||||
if (atomic_read(&cx->capturing) > 0 && cx->vbi.sliced_in->service_set == 0)
|
||||
return -EBUSY;
|
||||
cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
|
||||
memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_debug_ioctls(struct file *filp, unsigned int cmd, void *arg)
|
||||
{
|
||||
struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
|
||||
struct cx18 *cx = id->cx;
|
||||
struct v4l2_register *reg = arg;
|
||||
|
||||
switch (cmd) {
|
||||
/* ioctls to allow direct access to the encoder registers for testing */
|
||||
case VIDIOC_DBG_G_REGISTER:
|
||||
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
|
||||
return cx18_cxc(cx, cmd, arg);
|
||||
if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
|
||||
return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
|
||||
return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
|
||||
|
||||
case VIDIOC_DBG_S_REGISTER:
|
||||
if (v4l2_chip_match_host(reg->match_type, reg->match_chip))
|
||||
return cx18_cxc(cx, cmd, arg);
|
||||
if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
|
||||
return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
|
||||
return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
|
||||
|
||||
case VIDIOC_G_CHIP_IDENT: {
|
||||
struct v4l2_chip_ident *chip = arg;
|
||||
|
||||
chip->ident = V4L2_IDENT_NONE;
|
||||
chip->revision = 0;
|
||||
if (reg->match_type == V4L2_CHIP_MATCH_HOST) {
|
||||
if (v4l2_chip_match_host(reg->match_type, reg->match_chip)) {
|
||||
struct v4l2_chip_ident *chip = arg;
|
||||
|
||||
chip->ident = V4L2_IDENT_CX23418;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
if (reg->match_type == V4L2_CHIP_MATCH_I2C_DRIVER)
|
||||
return cx18_i2c_id(cx, reg->match_chip, cmd, arg);
|
||||
if (reg->match_type == V4L2_CHIP_MATCH_I2C_ADDR)
|
||||
return cx18_call_i2c_client(cx, reg->match_chip, cmd, arg);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case VIDIOC_INT_S_AUDIO_ROUTING: {
|
||||
struct v4l2_routing *route = arg;
|
||||
|
||||
cx18_audio_set_route(cx, route);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd, void *arg)
|
||||
{
|
||||
struct cx18_open_id *id = NULL;
|
||||
|
||||
if (filp)
|
||||
id = (struct cx18_open_id *)filp->private_data;
|
||||
|
||||
switch (cmd) {
|
||||
case VIDIOC_G_PRIORITY:
|
||||
{
|
||||
enum v4l2_priority *p = arg;
|
||||
|
||||
*p = v4l2_prio_max(&cx->prio);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_S_PRIORITY:
|
||||
{
|
||||
enum v4l2_priority *prio = arg;
|
||||
|
||||
return v4l2_prio_change(&cx->prio, &id->prio, *prio);
|
||||
}
|
||||
|
||||
case VIDIOC_QUERYCAP:{
|
||||
struct v4l2_capability *vcap = arg;
|
||||
|
||||
memset(vcap, 0, sizeof(*vcap));
|
||||
strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
|
||||
strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
|
||||
strlcpy(vcap->bus_info, pci_name(cx->dev), sizeof(vcap->bus_info));
|
||||
vcap->version = CX18_DRIVER_VERSION; /* version */
|
||||
vcap->capabilities = cx->v4l2_cap; /* capabilities */
|
||||
|
||||
/* reserved.. must set to 0! */
|
||||
vcap->reserved[0] = vcap->reserved[1] =
|
||||
vcap->reserved[2] = vcap->reserved[3] = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_ENUMAUDIO:{
|
||||
struct v4l2_audio *vin = arg;
|
||||
|
||||
return cx18_get_audio_input(cx, vin->index, vin);
|
||||
}
|
||||
|
||||
case VIDIOC_G_AUDIO:{
|
||||
struct v4l2_audio *vin = arg;
|
||||
|
||||
vin->index = cx->audio_input;
|
||||
return cx18_get_audio_input(cx, vin->index, vin);
|
||||
}
|
||||
|
||||
case VIDIOC_S_AUDIO:{
|
||||
struct v4l2_audio *vout = arg;
|
||||
|
||||
if (vout->index >= cx->nof_audio_inputs)
|
||||
return -EINVAL;
|
||||
cx->audio_input = vout->index;
|
||||
cx18_audio_set_io(cx);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_ENUMINPUT:{
|
||||
struct v4l2_input *vin = arg;
|
||||
|
||||
/* set it to defaults from our table */
|
||||
return cx18_get_input(cx, vin->index, vin);
|
||||
}
|
||||
|
||||
case VIDIOC_TRY_FMT:
|
||||
case VIDIOC_S_FMT: {
|
||||
struct v4l2_format *fmt = arg;
|
||||
|
||||
return cx18_try_or_set_fmt(cx, id->type, fmt, cmd == VIDIOC_S_FMT);
|
||||
}
|
||||
|
||||
case VIDIOC_G_FMT: {
|
||||
struct v4l2_format *fmt = arg;
|
||||
int type = fmt->type;
|
||||
|
||||
memset(fmt, 0, sizeof(*fmt));
|
||||
fmt->type = type;
|
||||
return cx18_get_fmt(cx, id->type, fmt);
|
||||
}
|
||||
|
||||
case VIDIOC_CROPCAP: {
|
||||
struct v4l2_cropcap *cropcap = arg;
|
||||
|
||||
if (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
||||
return -EINVAL;
|
||||
cropcap->bounds.top = cropcap->bounds.left = 0;
|
||||
cropcap->bounds.width = 720;
|
||||
cropcap->bounds.height = cx->is_50hz ? 576 : 480;
|
||||
cropcap->pixelaspect.numerator = cx->is_50hz ? 59 : 10;
|
||||
cropcap->pixelaspect.denominator = cx->is_50hz ? 54 : 11;
|
||||
cropcap->defrect = cropcap->bounds;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case VIDIOC_S_CROP: {
|
||||
struct v4l2_crop *crop = arg;
|
||||
|
||||
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
||||
return -EINVAL;
|
||||
return cx18_av_cmd(cx, VIDIOC_S_CROP, arg);
|
||||
}
|
||||
|
||||
case VIDIOC_G_CROP: {
|
||||
struct v4l2_crop *crop = arg;
|
||||
|
||||
if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
||||
return -EINVAL;
|
||||
return cx18_av_cmd(cx, VIDIOC_G_CROP, arg);
|
||||
}
|
||||
|
||||
case VIDIOC_ENUM_FMT: {
|
||||
static struct v4l2_fmtdesc formats[] = {
|
||||
{ 0, 0, 0,
|
||||
"HM12 (YUV 4:1:1)", V4L2_PIX_FMT_HM12,
|
||||
{ 0, 0, 0, 0 }
|
||||
},
|
||||
{ 1, 0, V4L2_FMT_FLAG_COMPRESSED,
|
||||
"MPEG", V4L2_PIX_FMT_MPEG,
|
||||
{ 0, 0, 0, 0 }
|
||||
}
|
||||
};
|
||||
struct v4l2_fmtdesc *fmt = arg;
|
||||
enum v4l2_buf_type type = fmt->type;
|
||||
|
||||
switch (type) {
|
||||
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
if (fmt->index > 1)
|
||||
return -EINVAL;
|
||||
*fmt = formats[fmt->index];
|
||||
fmt->type = type;
|
||||
return 0;
|
||||
}
|
||||
|
||||
case VIDIOC_G_INPUT:{
|
||||
*(int *)arg = cx->active_input;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_S_INPUT:{
|
||||
int inp = *(int *)arg;
|
||||
|
||||
if (inp < 0 || inp >= cx->nof_inputs)
|
||||
return -EINVAL;
|
||||
|
||||
if (inp == cx->active_input) {
|
||||
CX18_DEBUG_INFO("Input unchanged\n");
|
||||
break;
|
||||
}
|
||||
CX18_DEBUG_INFO("Changing input from %d to %d\n",
|
||||
cx->active_input, inp);
|
||||
|
||||
cx->active_input = inp;
|
||||
/* Set the audio input to whatever is appropriate for the
|
||||
input type. */
|
||||
cx->audio_input = cx->card->video_inputs[inp].audio_index;
|
||||
|
||||
/* prevent others from messing with the streams until
|
||||
we're finished changing inputs. */
|
||||
cx18_mute(cx);
|
||||
cx18_video_set_io(cx);
|
||||
cx18_audio_set_io(cx);
|
||||
cx18_unmute(cx);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_G_FREQUENCY:{
|
||||
struct v4l2_frequency *vf = arg;
|
||||
|
||||
if (vf->tuner != 0)
|
||||
return -EINVAL;
|
||||
cx18_call_i2c_clients(cx, cmd, arg);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_S_FREQUENCY:{
|
||||
struct v4l2_frequency vf = *(struct v4l2_frequency *)arg;
|
||||
|
||||
if (vf.tuner != 0)
|
||||
return -EINVAL;
|
||||
|
||||
cx18_mute(cx);
|
||||
CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf.frequency);
|
||||
cx18_call_i2c_clients(cx, cmd, &vf);
|
||||
cx18_unmute(cx);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_ENUMSTD:{
|
||||
struct v4l2_standard *vs = arg;
|
||||
int idx = vs->index;
|
||||
|
||||
if (idx < 0 || idx >= ARRAY_SIZE(enum_stds))
|
||||
return -EINVAL;
|
||||
|
||||
*vs = (enum_stds[idx].std & V4L2_STD_525_60) ?
|
||||
cx18_std_60hz : cx18_std_50hz;
|
||||
vs->index = idx;
|
||||
vs->id = enum_stds[idx].std;
|
||||
strlcpy(vs->name, enum_stds[idx].name, sizeof(vs->name));
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_G_STD:{
|
||||
*(v4l2_std_id *) arg = cx->std;
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_S_STD: {
|
||||
v4l2_std_id std = *(v4l2_std_id *) arg;
|
||||
|
||||
if ((std & V4L2_STD_ALL) == 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (std == cx->std)
|
||||
break;
|
||||
|
||||
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ||
|
||||
atomic_read(&cx->capturing) > 0) {
|
||||
/* Switching standard would turn off the radio or mess
|
||||
with already running streams, prevent that by
|
||||
returning EBUSY. */
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
cx->std = std;
|
||||
cx->is_60hz = (std & V4L2_STD_525_60) ? 1 : 0;
|
||||
cx->params.is_50hz = cx->is_50hz = !cx->is_60hz;
|
||||
cx->params.width = 720;
|
||||
cx->params.height = cx->is_50hz ? 576 : 480;
|
||||
cx->vbi.count = cx->is_50hz ? 18 : 12;
|
||||
cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
|
||||
cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
|
||||
cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
|
||||
CX18_DEBUG_INFO("Switching standard to %llx.\n", (unsigned long long)cx->std);
|
||||
|
||||
/* Tuner */
|
||||
cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_S_TUNER: { /* Setting tuner can only set audio mode */
|
||||
struct v4l2_tuner *vt = arg;
|
||||
|
||||
if (vt->index != 0)
|
||||
return -EINVAL;
|
||||
|
||||
cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_G_TUNER: {
|
||||
struct v4l2_tuner *vt = arg;
|
||||
|
||||
if (vt->index != 0)
|
||||
return -EINVAL;
|
||||
|
||||
memset(vt, 0, sizeof(*vt));
|
||||
cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
|
||||
|
||||
if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
|
||||
strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
|
||||
vt->type = V4L2_TUNER_RADIO;
|
||||
} else {
|
||||
strlcpy(vt->name, "cx18 TV Tuner", sizeof(vt->name));
|
||||
vt->type = V4L2_TUNER_ANALOG_TV;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_G_SLICED_VBI_CAP: {
|
||||
struct v4l2_sliced_vbi_cap *cap = arg;
|
||||
int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
|
||||
int f, l;
|
||||
enum v4l2_buf_type type = cap->type;
|
||||
|
||||
memset(cap, 0, sizeof(*cap));
|
||||
cap->type = type;
|
||||
if (type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
|
||||
for (f = 0; f < 2; f++) {
|
||||
for (l = 0; l < 24; l++) {
|
||||
if (valid_service_line(f, l, cx->is_50hz))
|
||||
cap->service_lines[f][l] = set;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
case VIDIOC_ENCODER_CMD:
|
||||
case VIDIOC_TRY_ENCODER_CMD: {
|
||||
struct v4l2_encoder_cmd *enc = arg;
|
||||
int try = cmd == VIDIOC_TRY_ENCODER_CMD;
|
||||
|
||||
memset(&enc->raw, 0, sizeof(enc->raw));
|
||||
switch (enc->cmd) {
|
||||
case V4L2_ENC_CMD_START:
|
||||
enc->flags = 0;
|
||||
if (try)
|
||||
return 0;
|
||||
return cx18_start_capture(id);
|
||||
|
||||
case V4L2_ENC_CMD_STOP:
|
||||
enc->flags &= V4L2_ENC_CMD_STOP_AT_GOP_END;
|
||||
if (try)
|
||||
return 0;
|
||||
cx18_stop_capture(id, enc->flags & V4L2_ENC_CMD_STOP_AT_GOP_END);
|
||||
return 0;
|
||||
|
||||
case V4L2_ENC_CMD_PAUSE:
|
||||
enc->flags = 0;
|
||||
if (try)
|
||||
return 0;
|
||||
if (!atomic_read(&cx->capturing))
|
||||
return -EPERM;
|
||||
if (test_and_set_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
|
||||
return 0;
|
||||
cx18_mute(cx);
|
||||
cx18_vapi(cx, CX18_CPU_CAPTURE_PAUSE, 1, cx18_find_handle(cx));
|
||||
break;
|
||||
|
||||
case V4L2_ENC_CMD_RESUME:
|
||||
enc->flags = 0;
|
||||
if (try)
|
||||
return 0;
|
||||
if (!atomic_read(&cx->capturing))
|
||||
return -EPERM;
|
||||
if (!test_and_clear_bit(CX18_F_I_ENC_PAUSED, &cx->i_flags))
|
||||
return 0;
|
||||
cx18_vapi(cx, CX18_CPU_CAPTURE_RESUME, 1, cx18_find_handle(cx));
|
||||
cx18_unmute(cx);
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case VIDIOC_LOG_STATUS:
|
||||
{
|
||||
struct v4l2_input vidin;
|
||||
struct v4l2_audio audin;
|
||||
int i;
|
||||
|
||||
CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num);
|
||||
if (cx->hw_flags & CX18_HW_TVEEPROM) {
|
||||
struct tveeprom tv;
|
||||
|
||||
cx18_read_eeprom(cx, &tv);
|
||||
}
|
||||
cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
|
||||
cx18_get_input(cx, cx->active_input, &vidin);
|
||||
cx18_get_audio_input(cx, cx->audio_input, &audin);
|
||||
CX18_INFO("Video Input: %s\n", vidin.name);
|
||||
CX18_INFO("Audio Input: %s\n", audin.name);
|
||||
CX18_INFO("Tuner: %s\n",
|
||||
test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ?
|
||||
"Radio" : "TV");
|
||||
cx2341x_log_status(&cx->params, cx->name);
|
||||
CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
|
||||
for (i = 0; i < CX18_MAX_STREAMS; i++) {
|
||||
struct cx18_stream *s = &cx->streams[i];
|
||||
|
||||
if (s->v4l2dev == NULL || s->buffers == 0)
|
||||
continue;
|
||||
CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
|
||||
s->name, s->s_flags,
|
||||
(s->buffers - s->q_free.buffers) * 100 / s->buffers,
|
||||
(s->buffers * s->buf_size) / 1024, s->buffers);
|
||||
}
|
||||
CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
|
||||
(long long)cx->mpg_data_received,
|
||||
(long long)cx->vbi_data_inserted);
|
||||
CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cx18_v4l2_do_ioctl(struct inode *inode, struct file *filp,
|
||||
unsigned int cmd, void *arg)
|
||||
{
|
||||
struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
|
||||
struct cx18 *cx = id->cx;
|
||||
int ret;
|
||||
|
||||
/* check priority */
|
||||
switch (cmd) {
|
||||
case VIDIOC_S_CTRL:
|
||||
case VIDIOC_S_STD:
|
||||
case VIDIOC_S_INPUT:
|
||||
case VIDIOC_S_TUNER:
|
||||
case VIDIOC_S_FREQUENCY:
|
||||
case VIDIOC_S_FMT:
|
||||
case VIDIOC_S_CROP:
|
||||
case VIDIOC_S_EXT_CTRLS:
|
||||
ret = v4l2_prio_check(&cx->prio, &id->prio);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
switch (cmd) {
|
||||
case VIDIOC_DBG_G_REGISTER:
|
||||
case VIDIOC_DBG_S_REGISTER:
|
||||
case VIDIOC_G_CHIP_IDENT:
|
||||
case VIDIOC_INT_S_AUDIO_ROUTING:
|
||||
case VIDIOC_INT_RESET:
|
||||
if (cx18_debug & CX18_DBGFLG_IOCTL) {
|
||||
printk(KERN_INFO "cx18%d ioctl: ", cx->num);
|
||||
v4l_printk_ioctl(cmd);
|
||||
}
|
||||
return cx18_debug_ioctls(filp, cmd, arg);
|
||||
|
||||
case VIDIOC_G_PRIORITY:
|
||||
case VIDIOC_S_PRIORITY:
|
||||
case VIDIOC_QUERYCAP:
|
||||
case VIDIOC_ENUMINPUT:
|
||||
case VIDIOC_G_INPUT:
|
||||
case VIDIOC_S_INPUT:
|
||||
case VIDIOC_G_FMT:
|
||||
case VIDIOC_S_FMT:
|
||||
case VIDIOC_TRY_FMT:
|
||||
case VIDIOC_ENUM_FMT:
|
||||
case VIDIOC_CROPCAP:
|
||||
case VIDIOC_G_CROP:
|
||||
case VIDIOC_S_CROP:
|
||||
case VIDIOC_G_FREQUENCY:
|
||||
case VIDIOC_S_FREQUENCY:
|
||||
case VIDIOC_ENUMSTD:
|
||||
case VIDIOC_G_STD:
|
||||
case VIDIOC_S_STD:
|
||||
case VIDIOC_S_TUNER:
|
||||
case VIDIOC_G_TUNER:
|
||||
case VIDIOC_ENUMAUDIO:
|
||||
case VIDIOC_S_AUDIO:
|
||||
case VIDIOC_G_AUDIO:
|
||||
case VIDIOC_G_SLICED_VBI_CAP:
|
||||
case VIDIOC_LOG_STATUS:
|
||||
case VIDIOC_G_ENC_INDEX:
|
||||
case VIDIOC_ENCODER_CMD:
|
||||
case VIDIOC_TRY_ENCODER_CMD:
|
||||
if (cx18_debug & CX18_DBGFLG_IOCTL) {
|
||||
printk(KERN_INFO "cx18%d ioctl: ", cx->num);
|
||||
v4l_printk_ioctl(cmd);
|
||||
}
|
||||
return cx18_v4l2_ioctls(cx, filp, cmd, arg);
|
||||
|
||||
case VIDIOC_QUERYMENU:
|
||||
case VIDIOC_QUERYCTRL:
|
||||
case VIDIOC_S_CTRL:
|
||||
case VIDIOC_G_CTRL:
|
||||
case VIDIOC_S_EXT_CTRLS:
|
||||
case VIDIOC_G_EXT_CTRLS:
|
||||
case VIDIOC_TRY_EXT_CTRLS:
|
||||
if (cx18_debug & CX18_DBGFLG_IOCTL) {
|
||||
printk(KERN_INFO "cx18%d ioctl: ", cx->num);
|
||||
v4l_printk_ioctl(cmd);
|
||||
}
|
||||
return cx18_control_ioctls(cx, cmd, arg);
|
||||
|
||||
case 0x00005401: /* Handle isatty() calls */
|
||||
return -EINVAL;
|
||||
default:
|
||||
return v4l_compat_translate_ioctl(inode, filp, cmd, arg,
|
||||
cx18_v4l2_do_ioctl);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct cx18_open_id *id = (struct cx18_open_id *)filp->private_data;
|
||||
struct cx18 *cx = id->cx;
|
||||
int res;
|
||||
|
||||
mutex_lock(&cx->serialize_lock);
|
||||
res = video_usercopy(inode, filp, cmd, arg, cx18_v4l2_do_ioctl);
|
||||
mutex_unlock(&cx->serialize_lock);
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* cx18 ioctl system call
|
||||
*
|
||||
* Derived from ivtv-ioctl.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
u16 cx18_service2vbi(int type);
|
||||
void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal);
|
||||
u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt);
|
||||
int cx18_v4l2_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
|
||||
unsigned long arg);
|
||||
int cx18_v4l2_ioctls(struct cx18 *cx, struct file *filp, unsigned cmd,
|
||||
void *arg);
|
|
@ -0,0 +1,179 @@
|
|||
/*
|
||||
* cx18 interrupt handling
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-firmware.h"
|
||||
#include "cx18-fileops.h"
|
||||
#include "cx18-queue.h"
|
||||
#include "cx18-irq.h"
|
||||
#include "cx18-ioctl.h"
|
||||
#include "cx18-mailbox.h"
|
||||
#include "cx18-vbi.h"
|
||||
#include "cx18-scb.h"
|
||||
|
||||
#define DMA_MAGIC_COOKIE 0x000001fe
|
||||
|
||||
static void epu_dma_done(struct cx18 *cx, struct cx18_mailbox *mb)
|
||||
{
|
||||
u32 handle = mb->args[0];
|
||||
struct cx18_stream *s = NULL;
|
||||
struct cx18_buffer *buf;
|
||||
u32 off;
|
||||
int i;
|
||||
int id;
|
||||
|
||||
for (i = 0; i < CX18_MAX_STREAMS; i++) {
|
||||
s = &cx->streams[i];
|
||||
if ((handle == s->handle) && (s->dvb.enabled))
|
||||
break;
|
||||
if (s->v4l2dev && handle == s->handle)
|
||||
break;
|
||||
}
|
||||
if (i == CX18_MAX_STREAMS) {
|
||||
CX18_WARN("DMA done for unknown handle %d for stream %s\n",
|
||||
handle, s->name);
|
||||
mb->error = CXERR_NOT_OPEN;
|
||||
mb->cmd = 0;
|
||||
cx18_mb_ack(cx, mb);
|
||||
return;
|
||||
}
|
||||
|
||||
off = mb->args[1];
|
||||
if (mb->args[2] != 1)
|
||||
CX18_WARN("Ack struct = %d for %s\n",
|
||||
mb->args[2], s->name);
|
||||
id = read_enc(off);
|
||||
buf = cx18_queue_find_buf(s, id, read_enc(off + 4));
|
||||
CX18_DEBUG_HI_DMA("DMA DONE for %s (buffer %d)\n", s->name, id);
|
||||
if (buf) {
|
||||
cx18_buf_sync_for_cpu(s, buf);
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_TS && s->dvb.enabled) {
|
||||
/* process the buffer here */
|
||||
CX18_DEBUG_HI_DMA("TS recv and sent bytesused=%d\n",
|
||||
buf->bytesused);
|
||||
|
||||
dvb_dmx_swfilter(&s->dvb.demux, buf->buf,
|
||||
buf->bytesused);
|
||||
|
||||
cx18_buf_sync_for_device(s, buf);
|
||||
cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
|
||||
(void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem,
|
||||
1, buf->id, s->buf_size);
|
||||
} else
|
||||
set_bit(CX18_F_B_NEED_BUF_SWAP, &buf->b_flags);
|
||||
} else {
|
||||
CX18_WARN("Could not find buf %d for stream %s\n",
|
||||
read_enc(off), s->name);
|
||||
}
|
||||
mb->error = 0;
|
||||
mb->cmd = 0;
|
||||
cx18_mb_ack(cx, mb);
|
||||
wake_up(&cx->dma_waitq);
|
||||
if (s->id != -1)
|
||||
wake_up(&s->waitq);
|
||||
}
|
||||
|
||||
static void epu_debug(struct cx18 *cx, struct cx18_mailbox *mb)
|
||||
{
|
||||
char str[256] = { 0 };
|
||||
char *p;
|
||||
|
||||
if (mb->args[1]) {
|
||||
setup_page(mb->args[1]);
|
||||
memcpy_fromio(str, cx->enc_mem + mb->args[1], 252);
|
||||
str[252] = 0;
|
||||
}
|
||||
cx18_mb_ack(cx, mb);
|
||||
CX18_DEBUG_INFO("%x %s\n", mb->args[0], str);
|
||||
p = strchr(str, '.');
|
||||
if (!test_bit(CX18_F_I_LOADED_FW, &cx->i_flags) && p && p > str)
|
||||
CX18_INFO("FW version: %s\n", p - 1);
|
||||
}
|
||||
|
||||
static void hpu_cmd(struct cx18 *cx, u32 sw1)
|
||||
{
|
||||
struct cx18_mailbox mb;
|
||||
|
||||
if (sw1 & IRQ_CPU_TO_EPU) {
|
||||
memcpy_fromio(&mb, &cx->scb->cpu2epu_mb, sizeof(mb));
|
||||
mb.error = 0;
|
||||
|
||||
switch (mb.cmd) {
|
||||
case CX18_EPU_DMA_DONE:
|
||||
epu_dma_done(cx, &mb);
|
||||
break;
|
||||
case CX18_EPU_DEBUG:
|
||||
epu_debug(cx, &mb);
|
||||
break;
|
||||
default:
|
||||
CX18_WARN("Unexpected mailbox command %08x\n", mb.cmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sw1 & (IRQ_APU_TO_EPU | IRQ_HPU_TO_EPU))
|
||||
CX18_WARN("Unexpected interrupt %08x\n", sw1);
|
||||
}
|
||||
|
||||
irqreturn_t cx18_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct cx18 *cx = (struct cx18 *)dev_id;
|
||||
u32 sw1, sw1_mask;
|
||||
u32 sw2, sw2_mask;
|
||||
u32 hw2, hw2_mask;
|
||||
|
||||
spin_lock(&cx->dma_reg_lock);
|
||||
|
||||
hw2_mask = read_reg(HW2_INT_MASK5_PCI);
|
||||
hw2 = read_reg(HW2_INT_CLR_STATUS) & hw2_mask;
|
||||
sw2_mask = read_reg(SW2_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU_ACK;
|
||||
sw2 = read_reg(SW2_INT_STATUS) & sw2_mask;
|
||||
sw1_mask = read_reg(SW1_INT_ENABLE_PCI) | IRQ_EPU_TO_HPU;
|
||||
sw1 = read_reg(SW1_INT_STATUS) & sw1_mask;
|
||||
|
||||
write_reg(sw2&sw2_mask, SW2_INT_STATUS);
|
||||
write_reg(sw1&sw1_mask, SW1_INT_STATUS);
|
||||
write_reg(hw2&hw2_mask, HW2_INT_CLR_STATUS);
|
||||
|
||||
if (sw1 || sw2 || hw2)
|
||||
CX18_DEBUG_HI_IRQ("SW1: %x SW2: %x HW2: %x\n", sw1, sw2, hw2);
|
||||
|
||||
/* To do: interrupt-based I2C handling
|
||||
if (hw2 & 0x00c00000) {
|
||||
}
|
||||
*/
|
||||
|
||||
if (sw2) {
|
||||
if (sw2 & (cx->scb->cpu2hpu_irq_ack | cx->scb->cpu2epu_irq_ack))
|
||||
wake_up(&cx->mb_cpu_waitq);
|
||||
if (sw2 & (cx->scb->apu2hpu_irq_ack | cx->scb->apu2epu_irq_ack))
|
||||
wake_up(&cx->mb_apu_waitq);
|
||||
if (sw2 & cx->scb->epu2hpu_irq_ack)
|
||||
wake_up(&cx->mb_epu_waitq);
|
||||
if (sw2 & cx->scb->hpu2epu_irq_ack)
|
||||
wake_up(&cx->mb_hpu_waitq);
|
||||
}
|
||||
|
||||
if (sw1)
|
||||
hpu_cmd(cx, sw1);
|
||||
spin_unlock(&cx->dma_reg_lock);
|
||||
|
||||
return (hw2 | sw1 | sw2) ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* cx18 interrupt handling
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define HW2_I2C1_INT (1 << 22)
|
||||
#define HW2_I2C2_INT (1 << 23)
|
||||
#define HW2_INT_CLR_STATUS 0xc730c4
|
||||
#define HW2_INT_MASK5_PCI 0xc730e4
|
||||
#define SW1_INT_SET 0xc73100
|
||||
#define SW1_INT_STATUS 0xc73104
|
||||
#define SW1_INT_ENABLE_PCI 0xc7311c
|
||||
#define SW2_INT_SET 0xc73140
|
||||
#define SW2_INT_STATUS 0xc73144
|
||||
#define SW2_INT_ENABLE_PCI 0xc7315c
|
||||
|
||||
irqreturn_t cx18_irq_handler(int irq, void *dev_id);
|
||||
|
||||
void cx18_irq_work_handler(struct work_struct *work);
|
||||
void cx18_dma_stream_dec_prepare(struct cx18_stream *s, u32 offset, int lock);
|
||||
void cx18_unfinished_dma(unsigned long arg);
|
|
@ -0,0 +1,372 @@
|
|||
/*
|
||||
* cx18 mailbox functions
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-scb.h"
|
||||
#include "cx18-irq.h"
|
||||
#include "cx18-mailbox.h"
|
||||
|
||||
#define API_FAST (1 << 2) /* Short timeout */
|
||||
#define API_SLOW (1 << 3) /* Additional 300ms timeout */
|
||||
|
||||
#define APU 0
|
||||
#define CPU 1
|
||||
#define EPU 2
|
||||
#define HPU 3
|
||||
|
||||
struct cx18_api_info {
|
||||
u32 cmd;
|
||||
u8 flags; /* Flags, see above */
|
||||
u8 rpu; /* Processing unit */
|
||||
const char *name; /* The name of the command */
|
||||
};
|
||||
|
||||
#define API_ENTRY(rpu, x, f) { (x), (f), (rpu), #x }
|
||||
|
||||
static const struct cx18_api_info api_info[] = {
|
||||
/* MPEG encoder API */
|
||||
API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
|
||||
API_ENTRY(CPU, CX18_EPU_DEBUG, 0),
|
||||
API_ENTRY(CPU, CX18_CREATE_TASK, 0),
|
||||
API_ENTRY(CPU, CX18_DESTROY_TASK, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_CAPTURE_START, API_SLOW),
|
||||
API_ENTRY(CPU, CX18_CPU_CAPTURE_STOP, API_SLOW),
|
||||
API_ENTRY(CPU, CX18_CPU_CAPTURE_PAUSE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_CAPTURE_RESUME, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_CHANNEL_TYPE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_VIDEO_IN, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RATE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_VIDEO_RESOLUTION, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_FILTER_PARAM, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_MEDIAN_CORING, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_INDEXTABLE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PARAMETERS, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_VIDEO_MUTE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_AUDIO_MUTE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_MISC_PARAMETERS, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_RAW_VBI_PARAM, API_SLOW),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_CAPTURE_LINE_NO, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_COPYRIGHT, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_AUDIO_PID, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_VIDEO_PID, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_VER_CROP_LINE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_GOP_STRUCTURE, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_SCENE_CHANGE_DETECTION, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_ASPECT_RATIO, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_SKIP_INPUT_FRAME, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_SLICED_VBI_PARAM, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_SET_USERDATA_PLACE_HOLDER, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_GET_ENC_PTS, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
|
||||
API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
|
||||
API_ENTRY(0, 0, 0),
|
||||
};
|
||||
|
||||
static const struct cx18_api_info *find_api_info(u32 cmd)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; api_info[i].cmd; i++)
|
||||
if (api_info[i].cmd == cmd)
|
||||
return &api_info[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static struct cx18_mailbox *cx18_mb_is_complete(struct cx18 *cx, int rpu,
|
||||
u32 *state, u32 *irq, u32 *req)
|
||||
{
|
||||
struct cx18_mailbox *mb = NULL;
|
||||
int wait_count = 0;
|
||||
u32 ack;
|
||||
|
||||
switch (rpu) {
|
||||
case APU:
|
||||
mb = &cx->scb->epu2apu_mb;
|
||||
*state = readl(&cx->scb->apu_state);
|
||||
*irq = readl(&cx->scb->epu2apu_irq);
|
||||
break;
|
||||
|
||||
case CPU:
|
||||
mb = &cx->scb->epu2cpu_mb;
|
||||
*state = readl(&cx->scb->cpu_state);
|
||||
*irq = readl(&cx->scb->epu2cpu_irq);
|
||||
break;
|
||||
|
||||
case HPU:
|
||||
mb = &cx->scb->epu2hpu_mb;
|
||||
*state = readl(&cx->scb->hpu_state);
|
||||
*irq = readl(&cx->scb->epu2hpu_irq);
|
||||
break;
|
||||
}
|
||||
|
||||
if (mb == NULL)
|
||||
return mb;
|
||||
|
||||
do {
|
||||
*req = readl(&mb->request);
|
||||
ack = readl(&mb->ack);
|
||||
wait_count++;
|
||||
} while (*req != ack && wait_count < 600);
|
||||
|
||||
if (*req == ack) {
|
||||
(*req)++;
|
||||
if (*req == 0 || *req == 0xffffffff)
|
||||
*req = 1;
|
||||
return mb;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb)
|
||||
{
|
||||
const struct cx18_api_info *info = find_api_info(mb->cmd);
|
||||
struct cx18_mailbox *ack_mb;
|
||||
u32 ack_irq;
|
||||
u8 rpu = CPU;
|
||||
|
||||
if (info == NULL && mb->cmd) {
|
||||
CX18_WARN("Cannot ack unknown command %x\n", mb->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
if (info)
|
||||
rpu = info->rpu;
|
||||
|
||||
switch (rpu) {
|
||||
case HPU:
|
||||
ack_irq = IRQ_EPU_TO_HPU_ACK;
|
||||
ack_mb = &cx->scb->hpu2epu_mb;
|
||||
break;
|
||||
case APU:
|
||||
ack_irq = IRQ_EPU_TO_APU_ACK;
|
||||
ack_mb = &cx->scb->apu2epu_mb;
|
||||
break;
|
||||
case CPU:
|
||||
ack_irq = IRQ_EPU_TO_CPU_ACK;
|
||||
ack_mb = &cx->scb->cpu2epu_mb;
|
||||
break;
|
||||
default:
|
||||
CX18_WARN("Unknown RPU for command %x\n", mb->cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
setup_page(SCB_OFFSET);
|
||||
write_sync(mb->request, &ack_mb->ack);
|
||||
write_reg(ack_irq, SW2_INT_SET);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
|
||||
{
|
||||
const struct cx18_api_info *info = find_api_info(cmd);
|
||||
u32 state = 0, irq = 0, req, oldreq, err;
|
||||
struct cx18_mailbox *mb;
|
||||
wait_queue_head_t *waitq;
|
||||
int timeout = 100;
|
||||
int cnt = 0;
|
||||
int sig = 0;
|
||||
int i;
|
||||
|
||||
if (info == NULL) {
|
||||
CX18_WARN("unknown cmd %x\n", cmd);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (cmd == CX18_CPU_DE_SET_MDL)
|
||||
CX18_DEBUG_HI_API("%s\n", info->name);
|
||||
else
|
||||
CX18_DEBUG_API("%s\n", info->name);
|
||||
setup_page(SCB_OFFSET);
|
||||
mb = cx18_mb_is_complete(cx, info->rpu, &state, &irq, &req);
|
||||
|
||||
if (mb == NULL) {
|
||||
CX18_ERR("mb %s busy\n", info->name);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
oldreq = req - 1;
|
||||
writel(cmd, &mb->cmd);
|
||||
for (i = 0; i < args; i++)
|
||||
writel(data[i], &mb->args[i]);
|
||||
writel(0, &mb->error);
|
||||
writel(req, &mb->request);
|
||||
|
||||
switch (info->rpu) {
|
||||
case APU: waitq = &cx->mb_apu_waitq; break;
|
||||
case CPU: waitq = &cx->mb_cpu_waitq; break;
|
||||
case EPU: waitq = &cx->mb_epu_waitq; break;
|
||||
case HPU: waitq = &cx->mb_hpu_waitq; break;
|
||||
default: return -EINVAL;
|
||||
}
|
||||
if (info->flags & API_FAST)
|
||||
timeout /= 2;
|
||||
write_reg(irq, SW1_INT_SET);
|
||||
|
||||
while (!sig && readl(&mb->ack) != readl(&mb->request) && cnt < 660) {
|
||||
if (cnt > 200 && !in_atomic())
|
||||
sig = cx18_msleep_timeout(10, 1);
|
||||
cnt++;
|
||||
}
|
||||
if (sig)
|
||||
return -EINTR;
|
||||
if (cnt == 660) {
|
||||
writel(oldreq, &mb->request);
|
||||
CX18_ERR("mb %s failed\n", info->name);
|
||||
return -EINVAL;
|
||||
}
|
||||
for (i = 0; i < MAX_MB_ARGUMENTS; i++)
|
||||
data[i] = readl(&mb->args[i]);
|
||||
err = readl(&mb->error);
|
||||
if (!in_atomic() && (info->flags & API_SLOW))
|
||||
cx18_msleep_timeout(300, 0);
|
||||
if (err)
|
||||
CX18_DEBUG_API("mailbox error %08x for command %s\n", err,
|
||||
info->name);
|
||||
return err ? -EIO : 0;
|
||||
}
|
||||
|
||||
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[])
|
||||
{
|
||||
int res = cx18_api_call(cx, cmd, args, data);
|
||||
|
||||
/* Allow a single retry, probably already too late though.
|
||||
If there is no free mailbox then that is usually an indication
|
||||
of a more serious problem. */
|
||||
return (res == -EBUSY) ? cx18_api_call(cx, cmd, args, data) : res;
|
||||
}
|
||||
|
||||
static int cx18_set_filter_param(struct cx18_stream *s)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
u32 mode;
|
||||
int ret;
|
||||
|
||||
mode = (cx->filter_mode & 1) ? 2 : (cx->spatial_strength ? 1 : 0);
|
||||
ret = cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
|
||||
s->handle, 1, mode, cx->spatial_strength);
|
||||
mode = (cx->filter_mode & 2) ? 2 : (cx->temporal_strength ? 1 : 0);
|
||||
ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
|
||||
s->handle, 0, mode, cx->temporal_strength);
|
||||
ret = ret ? ret : cx18_vapi(cx, CX18_CPU_SET_FILTER_PARAM, 4,
|
||||
s->handle, 2, cx->filter_mode >> 2, 0);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int cx18_api_func(void *priv, u32 cmd, int in, int out,
|
||||
u32 data[CX2341X_MBOX_MAX_DATA])
|
||||
{
|
||||
struct cx18 *cx = priv;
|
||||
struct cx18_stream *s = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
|
||||
|
||||
switch (cmd) {
|
||||
case CX2341X_ENC_SET_OUTPUT_PORT:
|
||||
return 0;
|
||||
case CX2341X_ENC_SET_FRAME_RATE:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_VIDEO_IN, 6,
|
||||
s->handle, 0, 0, 0, 0, data[0]);
|
||||
case CX2341X_ENC_SET_FRAME_SIZE:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RESOLUTION, 3,
|
||||
s->handle, data[1], data[0]);
|
||||
case CX2341X_ENC_SET_STREAM_TYPE:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_STREAM_OUTPUT_TYPE, 2,
|
||||
s->handle, data[0]);
|
||||
case CX2341X_ENC_SET_ASPECT_RATIO:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_ASPECT_RATIO, 2,
|
||||
s->handle, data[0]);
|
||||
|
||||
case CX2341X_ENC_SET_GOP_PROPERTIES:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_GOP_STRUCTURE, 3,
|
||||
s->handle, data[0], data[1]);
|
||||
case CX2341X_ENC_SET_GOP_CLOSURE:
|
||||
return 0;
|
||||
case CX2341X_ENC_SET_AUDIO_PROPERTIES:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_AUDIO_PARAMETERS, 2,
|
||||
s->handle, data[0]);
|
||||
case CX2341X_ENC_MUTE_AUDIO:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_AUDIO_MUTE, 2,
|
||||
s->handle, data[0]);
|
||||
case CX2341X_ENC_SET_BIT_RATE:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_VIDEO_RATE, 5,
|
||||
s->handle, data[0], data[1], data[2], data[3]);
|
||||
case CX2341X_ENC_MUTE_VIDEO:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
|
||||
s->handle, data[0]);
|
||||
case CX2341X_ENC_SET_FRAME_DROP_RATE:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_SKIP_INPUT_FRAME, 2,
|
||||
s->handle, data[0]);
|
||||
case CX2341X_ENC_MISC:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 4,
|
||||
s->handle, data[0], data[1], data[2]);
|
||||
case CX2341X_ENC_SET_DNR_FILTER_MODE:
|
||||
cx->filter_mode = (data[0] & 3) | (data[1] << 2);
|
||||
return cx18_set_filter_param(s);
|
||||
case CX2341X_ENC_SET_DNR_FILTER_PROPS:
|
||||
cx->spatial_strength = data[0];
|
||||
cx->temporal_strength = data[1];
|
||||
return cx18_set_filter_param(s);
|
||||
case CX2341X_ENC_SET_SPATIAL_FILTER_TYPE:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_SPATIAL_FILTER_TYPE, 3,
|
||||
s->handle, data[0], data[1]);
|
||||
case CX2341X_ENC_SET_CORING_LEVELS:
|
||||
return cx18_vapi(cx, CX18_CPU_SET_MEDIAN_CORING, 5,
|
||||
s->handle, data[0], data[1], data[2], data[3]);
|
||||
}
|
||||
CX18_WARN("Unknown cmd %x\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS],
|
||||
u32 cmd, int args, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
va_start(ap, args);
|
||||
for (i = 0; i < args; i++)
|
||||
data[i] = va_arg(ap, u32);
|
||||
va_end(ap);
|
||||
return cx18_api(cx, cmd, args, data);
|
||||
}
|
||||
|
||||
int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...)
|
||||
{
|
||||
u32 data[MAX_MB_ARGUMENTS];
|
||||
va_list ap;
|
||||
int i;
|
||||
|
||||
if (cx == NULL) {
|
||||
CX18_ERR("cx == NULL (cmd=%x)\n", cmd);
|
||||
return 0;
|
||||
}
|
||||
if (args > MAX_MB_ARGUMENTS) {
|
||||
CX18_ERR("args too big (cmd=%x)\n", cmd);
|
||||
args = MAX_MB_ARGUMENTS;
|
||||
}
|
||||
va_start(ap, args);
|
||||
for (i = 0; i < args; i++)
|
||||
data[i] = va_arg(ap, u32);
|
||||
va_end(ap);
|
||||
return cx18_api(cx, cmd, args, data);
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* cx18 mailbox functions
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef _CX18_MAILBOX_H_
|
||||
#define _CX18_MAILBOX_H_
|
||||
|
||||
/* mailbox max args */
|
||||
#define MAX_MB_ARGUMENTS 6
|
||||
/* compatibility, should be same as the define in cx2341x.h */
|
||||
#define CX2341X_MBOX_MAX_DATA 16
|
||||
|
||||
#define MB_RESERVED_HANDLE_0 0
|
||||
#define MB_RESERVED_HANDLE_1 0xFFFFFFFF
|
||||
|
||||
struct cx18;
|
||||
|
||||
/* The cx18_mailbox struct is the mailbox structure which is used for passing
|
||||
messages between processors */
|
||||
struct cx18_mailbox {
|
||||
/* The sender sets a handle in 'request' after he fills the command. The
|
||||
'request' should be different than 'ack'. The sender, also, generates
|
||||
an interrupt on XPU2YPU_irq where XPU is the sender and YPU is the
|
||||
receiver. */
|
||||
u32 request;
|
||||
/* The receiver detects a new command when 'req' is different than 'ack'.
|
||||
He sets 'ack' to the same value as 'req' to clear the command. He, also,
|
||||
generates an interrupt on YPU2XPU_irq where XPU is the sender and YPU
|
||||
is the receiver. */
|
||||
u32 ack;
|
||||
u32 reserved[6];
|
||||
/* 'cmd' identifies the command. The list of these commands are in
|
||||
cx23418.h */
|
||||
u32 cmd;
|
||||
/* Each command can have up to 6 arguments */
|
||||
u32 args[MAX_MB_ARGUMENTS];
|
||||
/* The return code can be one of the codes in the file cx23418.h. If the
|
||||
command is completed successfuly, the error will be ERR_SYS_SUCCESS.
|
||||
If it is pending, the code is ERR_SYS_PENDING. If it failed, the error
|
||||
code would indicate the task from which the error originated and will
|
||||
be one of the errors in cx23418.h. In that case, the following
|
||||
applies ((error & 0xff) != 0).
|
||||
If the command is pending, the return will be passed in a MB from the
|
||||
receiver to the sender. 'req' will be returned in args[0] */
|
||||
u32 error;
|
||||
};
|
||||
|
||||
int cx18_api(struct cx18 *cx, u32 cmd, int args, u32 data[]);
|
||||
int cx18_vapi_result(struct cx18 *cx, u32 data[MAX_MB_ARGUMENTS], u32 cmd,
|
||||
int args, ...);
|
||||
int cx18_vapi(struct cx18 *cx, u32 cmd, int args, ...);
|
||||
int cx18_api_func(void *priv, u32 cmd, int in, int out,
|
||||
u32 data[CX2341X_MBOX_MAX_DATA]);
|
||||
long cx18_mb_ack(struct cx18 *cx, const struct cx18_mailbox *mb);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,282 @@
|
|||
/*
|
||||
* cx18 buffer queues
|
||||
*
|
||||
* Derived from ivtv-queue.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-streams.h"
|
||||
#include "cx18-queue.h"
|
||||
#include "cx18-scb.h"
|
||||
|
||||
int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
|
||||
const char __user *src, int copybytes)
|
||||
{
|
||||
if (s->buf_size - buf->bytesused < copybytes)
|
||||
copybytes = s->buf_size - buf->bytesused;
|
||||
if (copy_from_user(buf->buf + buf->bytesused, src, copybytes))
|
||||
return -EFAULT;
|
||||
buf->bytesused += copybytes;
|
||||
return copybytes;
|
||||
}
|
||||
|
||||
void cx18_buf_swap(struct cx18_buffer *buf)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < buf->bytesused; i += 4)
|
||||
swab32s((u32 *)(buf->buf + i));
|
||||
}
|
||||
|
||||
void cx18_queue_init(struct cx18_queue *q)
|
||||
{
|
||||
INIT_LIST_HEAD(&q->list);
|
||||
q->buffers = 0;
|
||||
q->length = 0;
|
||||
q->bytesused = 0;
|
||||
}
|
||||
|
||||
void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
|
||||
struct cx18_queue *q)
|
||||
{
|
||||
unsigned long flags = 0;
|
||||
|
||||
/* clear the buffer if it is going to be enqueued to the free queue */
|
||||
if (q == &s->q_free) {
|
||||
buf->bytesused = 0;
|
||||
buf->readpos = 0;
|
||||
buf->b_flags = 0;
|
||||
}
|
||||
spin_lock_irqsave(&s->qlock, flags);
|
||||
list_add_tail(&buf->list, &q->list);
|
||||
q->buffers++;
|
||||
q->length += s->buf_size;
|
||||
q->bytesused += buf->bytesused - buf->readpos;
|
||||
spin_unlock_irqrestore(&s->qlock, flags);
|
||||
}
|
||||
|
||||
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q)
|
||||
{
|
||||
struct cx18_buffer *buf = NULL;
|
||||
unsigned long flags = 0;
|
||||
|
||||
spin_lock_irqsave(&s->qlock, flags);
|
||||
if (!list_empty(&q->list)) {
|
||||
buf = list_entry(q->list.next, struct cx18_buffer, list);
|
||||
list_del_init(q->list.next);
|
||||
q->buffers--;
|
||||
q->length -= s->buf_size;
|
||||
q->bytesused -= buf->bytesused - buf->readpos;
|
||||
}
|
||||
spin_unlock_irqrestore(&s->qlock, flags);
|
||||
return buf;
|
||||
}
|
||||
|
||||
struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
|
||||
u32 bytesused)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
struct list_head *p;
|
||||
|
||||
list_for_each(p, &s->q_free.list) {
|
||||
struct cx18_buffer *buf =
|
||||
list_entry(p, struct cx18_buffer, list);
|
||||
|
||||
if (buf->id != id)
|
||||
continue;
|
||||
buf->bytesused = bytesused;
|
||||
/* the transport buffers are handled differently,
|
||||
so there is no need to move them to the full queue */
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_TS)
|
||||
return buf;
|
||||
s->q_free.buffers--;
|
||||
s->q_free.length -= s->buf_size;
|
||||
s->q_full.buffers++;
|
||||
s->q_full.length += s->buf_size;
|
||||
s->q_full.bytesused += buf->bytesused;
|
||||
list_move_tail(&buf->list, &s->q_full.list);
|
||||
return buf;
|
||||
}
|
||||
CX18_ERR("Cannot find buffer %d for stream %s\n", id, s->name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void cx18_queue_move_buf(struct cx18_stream *s, struct cx18_queue *from,
|
||||
struct cx18_queue *to, int clear, int full)
|
||||
{
|
||||
struct cx18_buffer *buf =
|
||||
list_entry(from->list.next, struct cx18_buffer, list);
|
||||
|
||||
list_move_tail(from->list.next, &to->list);
|
||||
from->buffers--;
|
||||
from->length -= s->buf_size;
|
||||
from->bytesused -= buf->bytesused - buf->readpos;
|
||||
/* special handling for q_free */
|
||||
if (clear)
|
||||
buf->bytesused = buf->readpos = buf->b_flags = 0;
|
||||
else if (full) {
|
||||
/* special handling for stolen buffers, assume
|
||||
all bytes are used. */
|
||||
buf->bytesused = s->buf_size;
|
||||
buf->readpos = buf->b_flags = 0;
|
||||
}
|
||||
to->buffers++;
|
||||
to->length += s->buf_size;
|
||||
to->bytesused += buf->bytesused - buf->readpos;
|
||||
}
|
||||
|
||||
/* Move 'needed_bytes' worth of buffers from queue 'from' into queue 'to'.
|
||||
If 'needed_bytes' == 0, then move all buffers from 'from' into 'to'.
|
||||
If 'steal' != NULL, then buffers may also taken from that queue if
|
||||
needed.
|
||||
|
||||
The buffer is automatically cleared if it goes to the free queue. It is
|
||||
also cleared if buffers need to be taken from the 'steal' queue and
|
||||
the 'from' queue is the free queue.
|
||||
|
||||
When 'from' is q_free, then needed_bytes is compared to the total
|
||||
available buffer length, otherwise needed_bytes is compared to the
|
||||
bytesused value. For the 'steal' queue the total available buffer
|
||||
length is always used.
|
||||
|
||||
-ENOMEM is returned if the buffers could not be obtained, 0 if all
|
||||
buffers where obtained from the 'from' list and if non-zero then
|
||||
the number of stolen buffers is returned. */
|
||||
int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
|
||||
struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes)
|
||||
{
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
int from_free = from == &s->q_free;
|
||||
int to_free = to == &s->q_free;
|
||||
int bytes_available;
|
||||
|
||||
spin_lock_irqsave(&s->qlock, flags);
|
||||
if (needed_bytes == 0) {
|
||||
from_free = 1;
|
||||
needed_bytes = from->length;
|
||||
}
|
||||
|
||||
bytes_available = from_free ? from->length : from->bytesused;
|
||||
bytes_available += steal ? steal->length : 0;
|
||||
|
||||
if (bytes_available < needed_bytes) {
|
||||
spin_unlock_irqrestore(&s->qlock, flags);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (from_free) {
|
||||
u32 old_length = to->length;
|
||||
|
||||
while (to->length - old_length < needed_bytes) {
|
||||
if (list_empty(&from->list))
|
||||
from = steal;
|
||||
if (from == steal)
|
||||
rc++; /* keep track of 'stolen' buffers */
|
||||
cx18_queue_move_buf(s, from, to, 1, 0);
|
||||
}
|
||||
} else {
|
||||
u32 old_bytesused = to->bytesused;
|
||||
|
||||
while (to->bytesused - old_bytesused < needed_bytes) {
|
||||
if (list_empty(&from->list))
|
||||
from = steal;
|
||||
if (from == steal)
|
||||
rc++; /* keep track of 'stolen' buffers */
|
||||
cx18_queue_move_buf(s, from, to, to_free, rc);
|
||||
}
|
||||
}
|
||||
spin_unlock_irqrestore(&s->qlock, flags);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void cx18_flush_queues(struct cx18_stream *s)
|
||||
{
|
||||
cx18_queue_move(s, &s->q_io, NULL, &s->q_free, 0);
|
||||
cx18_queue_move(s, &s->q_full, NULL, &s->q_free, 0);
|
||||
}
|
||||
|
||||
int cx18_stream_alloc(struct cx18_stream *s)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
int i;
|
||||
|
||||
if (s->buffers == 0)
|
||||
return 0;
|
||||
|
||||
CX18_DEBUG_INFO("Allocate %s stream: %d x %d buffers (%dkB total)\n",
|
||||
s->name, s->buffers, s->buf_size,
|
||||
s->buffers * s->buf_size / 1024);
|
||||
|
||||
if (((char *)&cx->scb->cpu_mdl[cx->mdl_offset + s->buffers] -
|
||||
(char *)cx->scb) > SCB_RESERVED_SIZE) {
|
||||
unsigned bufsz = (((char *)cx->scb) + SCB_RESERVED_SIZE -
|
||||
((char *)cx->scb->cpu_mdl));
|
||||
|
||||
CX18_ERR("Too many buffers, cannot fit in SCB area\n");
|
||||
CX18_ERR("Max buffers = %zd\n",
|
||||
bufsz / sizeof(struct cx18_mdl));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
s->mdl_offset = cx->mdl_offset;
|
||||
|
||||
/* allocate stream buffers. Initially all buffers are in q_free. */
|
||||
for (i = 0; i < s->buffers; i++) {
|
||||
struct cx18_buffer *buf =
|
||||
kzalloc(sizeof(struct cx18_buffer), GFP_KERNEL);
|
||||
|
||||
if (buf == NULL)
|
||||
break;
|
||||
buf->buf = kmalloc(s->buf_size, GFP_KERNEL);
|
||||
if (buf->buf == NULL) {
|
||||
kfree(buf);
|
||||
break;
|
||||
}
|
||||
buf->id = cx->buffer_id++;
|
||||
INIT_LIST_HEAD(&buf->list);
|
||||
buf->dma_handle = pci_map_single(s->cx->dev,
|
||||
buf->buf, s->buf_size, s->dma);
|
||||
cx18_buf_sync_for_cpu(s, buf);
|
||||
cx18_enqueue(s, buf, &s->q_free);
|
||||
}
|
||||
if (i == s->buffers) {
|
||||
cx->mdl_offset += s->buffers;
|
||||
return 0;
|
||||
}
|
||||
CX18_ERR("Couldn't allocate buffers for %s stream\n", s->name);
|
||||
cx18_stream_free(s);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
void cx18_stream_free(struct cx18_stream *s)
|
||||
{
|
||||
struct cx18_buffer *buf;
|
||||
|
||||
/* move all buffers to q_free */
|
||||
cx18_flush_queues(s);
|
||||
|
||||
/* empty q_free */
|
||||
while ((buf = cx18_dequeue(s, &s->q_free))) {
|
||||
pci_unmap_single(s->cx->dev, buf->dma_handle,
|
||||
s->buf_size, s->dma);
|
||||
kfree(buf->buf);
|
||||
kfree(buf);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* cx18 buffer queues
|
||||
*
|
||||
* Derived from ivtv-queue.h
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#define CX18_DMA_UNMAPPED ((u32) -1)
|
||||
|
||||
/* cx18_buffer utility functions */
|
||||
|
||||
static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
|
||||
struct cx18_buffer *buf)
|
||||
{
|
||||
pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle,
|
||||
s->buf_size, s->dma);
|
||||
}
|
||||
|
||||
static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
|
||||
struct cx18_buffer *buf)
|
||||
{
|
||||
pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle,
|
||||
s->buf_size, s->dma);
|
||||
}
|
||||
|
||||
int cx18_buf_copy_from_user(struct cx18_stream *s, struct cx18_buffer *buf,
|
||||
const char __user *src, int copybytes);
|
||||
void cx18_buf_swap(struct cx18_buffer *buf);
|
||||
|
||||
/* cx18_queue utility functions */
|
||||
void cx18_queue_init(struct cx18_queue *q);
|
||||
void cx18_enqueue(struct cx18_stream *s, struct cx18_buffer *buf,
|
||||
struct cx18_queue *q);
|
||||
struct cx18_buffer *cx18_dequeue(struct cx18_stream *s, struct cx18_queue *q);
|
||||
int cx18_queue_move(struct cx18_stream *s, struct cx18_queue *from,
|
||||
struct cx18_queue *steal, struct cx18_queue *to, int needed_bytes);
|
||||
struct cx18_buffer *cx18_queue_find_buf(struct cx18_stream *s, u32 id,
|
||||
u32 bytesused);
|
||||
void cx18_flush_queues(struct cx18_stream *s);
|
||||
|
||||
/* cx18_stream utility functions */
|
||||
int cx18_stream_alloc(struct cx18_stream *s);
|
||||
void cx18_stream_free(struct cx18_stream *s);
|
|
@ -0,0 +1,121 @@
|
|||
/*
|
||||
* cx18 System Control Block initialization
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-scb.h"
|
||||
|
||||
void cx18_init_scb(struct cx18 *cx)
|
||||
{
|
||||
setup_page(SCB_OFFSET);
|
||||
memset_io(cx->scb, 0, 0x10000);
|
||||
|
||||
writel(IRQ_APU_TO_CPU, &cx->scb->apu2cpu_irq);
|
||||
writel(IRQ_CPU_TO_APU_ACK, &cx->scb->cpu2apu_irq_ack);
|
||||
writel(IRQ_HPU_TO_CPU, &cx->scb->hpu2cpu_irq);
|
||||
writel(IRQ_CPU_TO_HPU_ACK, &cx->scb->cpu2hpu_irq_ack);
|
||||
writel(IRQ_PPU_TO_CPU, &cx->scb->ppu2cpu_irq);
|
||||
writel(IRQ_CPU_TO_PPU_ACK, &cx->scb->cpu2ppu_irq_ack);
|
||||
writel(IRQ_EPU_TO_CPU, &cx->scb->epu2cpu_irq);
|
||||
writel(IRQ_CPU_TO_EPU_ACK, &cx->scb->cpu2epu_irq_ack);
|
||||
|
||||
writel(IRQ_CPU_TO_APU, &cx->scb->cpu2apu_irq);
|
||||
writel(IRQ_APU_TO_CPU_ACK, &cx->scb->apu2cpu_irq_ack);
|
||||
writel(IRQ_HPU_TO_APU, &cx->scb->hpu2apu_irq);
|
||||
writel(IRQ_APU_TO_HPU_ACK, &cx->scb->apu2hpu_irq_ack);
|
||||
writel(IRQ_PPU_TO_APU, &cx->scb->ppu2apu_irq);
|
||||
writel(IRQ_APU_TO_PPU_ACK, &cx->scb->apu2ppu_irq_ack);
|
||||
writel(IRQ_EPU_TO_APU, &cx->scb->epu2apu_irq);
|
||||
writel(IRQ_APU_TO_EPU_ACK, &cx->scb->apu2epu_irq_ack);
|
||||
|
||||
writel(IRQ_CPU_TO_HPU, &cx->scb->cpu2hpu_irq);
|
||||
writel(IRQ_HPU_TO_CPU_ACK, &cx->scb->hpu2cpu_irq_ack);
|
||||
writel(IRQ_APU_TO_HPU, &cx->scb->apu2hpu_irq);
|
||||
writel(IRQ_HPU_TO_APU_ACK, &cx->scb->hpu2apu_irq_ack);
|
||||
writel(IRQ_PPU_TO_HPU, &cx->scb->ppu2hpu_irq);
|
||||
writel(IRQ_HPU_TO_PPU_ACK, &cx->scb->hpu2ppu_irq_ack);
|
||||
writel(IRQ_EPU_TO_HPU, &cx->scb->epu2hpu_irq);
|
||||
writel(IRQ_HPU_TO_EPU_ACK, &cx->scb->hpu2epu_irq_ack);
|
||||
|
||||
writel(IRQ_CPU_TO_PPU, &cx->scb->cpu2ppu_irq);
|
||||
writel(IRQ_PPU_TO_CPU_ACK, &cx->scb->ppu2cpu_irq_ack);
|
||||
writel(IRQ_APU_TO_PPU, &cx->scb->apu2ppu_irq);
|
||||
writel(IRQ_PPU_TO_APU_ACK, &cx->scb->ppu2apu_irq_ack);
|
||||
writel(IRQ_HPU_TO_PPU, &cx->scb->hpu2ppu_irq);
|
||||
writel(IRQ_PPU_TO_HPU_ACK, &cx->scb->ppu2hpu_irq_ack);
|
||||
writel(IRQ_EPU_TO_PPU, &cx->scb->epu2ppu_irq);
|
||||
writel(IRQ_PPU_TO_EPU_ACK, &cx->scb->ppu2epu_irq_ack);
|
||||
|
||||
writel(IRQ_CPU_TO_EPU, &cx->scb->cpu2epu_irq);
|
||||
writel(IRQ_EPU_TO_CPU_ACK, &cx->scb->epu2cpu_irq_ack);
|
||||
writel(IRQ_APU_TO_EPU, &cx->scb->apu2epu_irq);
|
||||
writel(IRQ_EPU_TO_APU_ACK, &cx->scb->epu2apu_irq_ack);
|
||||
writel(IRQ_HPU_TO_EPU, &cx->scb->hpu2epu_irq);
|
||||
writel(IRQ_EPU_TO_HPU_ACK, &cx->scb->epu2hpu_irq_ack);
|
||||
writel(IRQ_PPU_TO_EPU, &cx->scb->ppu2epu_irq);
|
||||
writel(IRQ_EPU_TO_PPU_ACK, &cx->scb->epu2ppu_irq_ack);
|
||||
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2cpu_mb),
|
||||
&cx->scb->apu2cpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2cpu_mb),
|
||||
&cx->scb->hpu2cpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2cpu_mb),
|
||||
&cx->scb->ppu2cpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2cpu_mb),
|
||||
&cx->scb->epu2cpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2apu_mb),
|
||||
&cx->scb->cpu2apu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2apu_mb),
|
||||
&cx->scb->hpu2apu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2apu_mb),
|
||||
&cx->scb->ppu2apu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2apu_mb),
|
||||
&cx->scb->epu2apu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2hpu_mb),
|
||||
&cx->scb->cpu2hpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2hpu_mb),
|
||||
&cx->scb->apu2hpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2hpu_mb),
|
||||
&cx->scb->ppu2hpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2hpu_mb),
|
||||
&cx->scb->epu2hpu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2ppu_mb),
|
||||
&cx->scb->cpu2ppu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2ppu_mb),
|
||||
&cx->scb->apu2ppu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2ppu_mb),
|
||||
&cx->scb->hpu2ppu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, epu2ppu_mb),
|
||||
&cx->scb->epu2ppu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu2epu_mb),
|
||||
&cx->scb->cpu2epu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, apu2epu_mb),
|
||||
&cx->scb->apu2epu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, hpu2epu_mb),
|
||||
&cx->scb->hpu2epu_mb_offset);
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, ppu2epu_mb),
|
||||
&cx->scb->ppu2epu_mb_offset);
|
||||
|
||||
writel(SCB_OFFSET + offsetof(struct cx18_scb, cpu_state),
|
||||
&cx->scb->ipc_offset);
|
||||
|
||||
writel(1, &cx->scb->hpu_state);
|
||||
writel(1, &cx->scb->epu_state);
|
||||
}
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* cx18 System Control Block initialization
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#ifndef CX18_SCB_H
|
||||
#define CX18_SCB_H
|
||||
|
||||
#include "cx18-mailbox.h"
|
||||
|
||||
/* NOTE: All ACK interrupts are in the SW2 register. All non-ACK interrupts
|
||||
are in the SW1 register. */
|
||||
|
||||
#define IRQ_APU_TO_CPU 0x00000001
|
||||
#define IRQ_CPU_TO_APU_ACK 0x00000001
|
||||
#define IRQ_HPU_TO_CPU 0x00000002
|
||||
#define IRQ_CPU_TO_HPU_ACK 0x00000002
|
||||
#define IRQ_PPU_TO_CPU 0x00000004
|
||||
#define IRQ_CPU_TO_PPU_ACK 0x00000004
|
||||
#define IRQ_EPU_TO_CPU 0x00000008
|
||||
#define IRQ_CPU_TO_EPU_ACK 0x00000008
|
||||
|
||||
#define IRQ_CPU_TO_APU 0x00000010
|
||||
#define IRQ_APU_TO_CPU_ACK 0x00000010
|
||||
#define IRQ_HPU_TO_APU 0x00000020
|
||||
#define IRQ_APU_TO_HPU_ACK 0x00000020
|
||||
#define IRQ_PPU_TO_APU 0x00000040
|
||||
#define IRQ_APU_TO_PPU_ACK 0x00000040
|
||||
#define IRQ_EPU_TO_APU 0x00000080
|
||||
#define IRQ_APU_TO_EPU_ACK 0x00000080
|
||||
|
||||
#define IRQ_CPU_TO_HPU 0x00000100
|
||||
#define IRQ_HPU_TO_CPU_ACK 0x00000100
|
||||
#define IRQ_APU_TO_HPU 0x00000200
|
||||
#define IRQ_HPU_TO_APU_ACK 0x00000200
|
||||
#define IRQ_PPU_TO_HPU 0x00000400
|
||||
#define IRQ_HPU_TO_PPU_ACK 0x00000400
|
||||
#define IRQ_EPU_TO_HPU 0x00000800
|
||||
#define IRQ_HPU_TO_EPU_ACK 0x00000800
|
||||
|
||||
#define IRQ_CPU_TO_PPU 0x00001000
|
||||
#define IRQ_PPU_TO_CPU_ACK 0x00001000
|
||||
#define IRQ_APU_TO_PPU 0x00002000
|
||||
#define IRQ_PPU_TO_APU_ACK 0x00002000
|
||||
#define IRQ_HPU_TO_PPU 0x00004000
|
||||
#define IRQ_PPU_TO_HPU_ACK 0x00004000
|
||||
#define IRQ_EPU_TO_PPU 0x00008000
|
||||
#define IRQ_PPU_TO_EPU_ACK 0x00008000
|
||||
|
||||
#define IRQ_CPU_TO_EPU 0x00010000
|
||||
#define IRQ_EPU_TO_CPU_ACK 0x00010000
|
||||
#define IRQ_APU_TO_EPU 0x00020000
|
||||
#define IRQ_EPU_TO_APU_ACK 0x00020000
|
||||
#define IRQ_HPU_TO_EPU 0x00040000
|
||||
#define IRQ_EPU_TO_HPU_ACK 0x00040000
|
||||
#define IRQ_PPU_TO_EPU 0x00080000
|
||||
#define IRQ_EPU_TO_PPU_ACK 0x00080000
|
||||
|
||||
#define SCB_OFFSET 0xDC0000
|
||||
|
||||
/* If Firmware uses fixed memory map, it shall not allocate the area
|
||||
between SCB_OFFSET and SCB_OFFSET+SCB_RESERVED_SIZE-1 inclusive */
|
||||
#define SCB_RESERVED_SIZE 0x10000
|
||||
|
||||
|
||||
/* This structure is used by EPU to provide memory descriptors in its memory */
|
||||
struct cx18_mdl {
|
||||
u32 paddr; /* Physical address of a buffer segment */
|
||||
u32 length; /* Length of the buffer segment */
|
||||
};
|
||||
|
||||
/* This structure is used by CPU to provide completed buffers information */
|
||||
struct cx18_mdl_ack {
|
||||
u32 id; /* ID of a completed MDL */
|
||||
u32 data_used; /* Total data filled in the MDL for buffer 'id' */
|
||||
};
|
||||
|
||||
struct cx18_scb {
|
||||
/* These fields form the System Control Block which is used at boot time
|
||||
for localizing the IPC data as well as the code positions for all
|
||||
processors. The offsets are from the start of this struct. */
|
||||
|
||||
/* Offset where to find the Inter-Processor Communication data */
|
||||
u32 ipc_offset;
|
||||
u32 reserved01[7];
|
||||
/* Offset where to find the start of the CPU code */
|
||||
u32 cpu_code_offset;
|
||||
u32 reserved02[3];
|
||||
/* Offset where to find the start of the APU code */
|
||||
u32 apu_code_offset;
|
||||
u32 reserved03[3];
|
||||
/* Offset where to find the start of the HPU code */
|
||||
u32 hpu_code_offset;
|
||||
u32 reserved04[3];
|
||||
/* Offset where to find the start of the PPU code */
|
||||
u32 ppu_code_offset;
|
||||
u32 reserved05[3];
|
||||
|
||||
/* These fields form Inter-Processor Communication data which is used
|
||||
by all processors to locate the information needed for communicating
|
||||
with other processors */
|
||||
|
||||
/* Fields for CPU: */
|
||||
|
||||
/* bit 0: 1/0 processor ready/not ready. Set other bits to 0. */
|
||||
u32 cpu_state;
|
||||
u32 reserved1[7];
|
||||
/* Offset to the mailbox used for sending commands from APU to CPU */
|
||||
u32 apu2cpu_mb_offset;
|
||||
/* Value to write to register SW1 register set (0xC7003100) after the
|
||||
command is ready */
|
||||
u32 apu2cpu_irq;
|
||||
/* Value to write to register SW2 register set (0xC7003140) after the
|
||||
command is cleared */
|
||||
u32 apu2cpu_irq_ack;
|
||||
u32 reserved2[13];
|
||||
|
||||
u32 hpu2cpu_mb_offset;
|
||||
u32 hpu2cpu_irq;
|
||||
u32 hpu2cpu_irq_ack;
|
||||
u32 reserved3[13];
|
||||
|
||||
u32 ppu2cpu_mb_offset;
|
||||
u32 ppu2cpu_irq;
|
||||
u32 ppu2cpu_irq_ack;
|
||||
u32 reserved4[13];
|
||||
|
||||
u32 epu2cpu_mb_offset;
|
||||
u32 epu2cpu_irq;
|
||||
u32 epu2cpu_irq_ack;
|
||||
u32 reserved5[13];
|
||||
u32 reserved6[8];
|
||||
|
||||
/* Fields for APU: */
|
||||
|
||||
u32 apu_state;
|
||||
u32 reserved11[7];
|
||||
u32 cpu2apu_mb_offset;
|
||||
u32 cpu2apu_irq;
|
||||
u32 cpu2apu_irq_ack;
|
||||
u32 reserved12[13];
|
||||
|
||||
u32 hpu2apu_mb_offset;
|
||||
u32 hpu2apu_irq;
|
||||
u32 hpu2apu_irq_ack;
|
||||
u32 reserved13[13];
|
||||
|
||||
u32 ppu2apu_mb_offset;
|
||||
u32 ppu2apu_irq;
|
||||
u32 ppu2apu_irq_ack;
|
||||
u32 reserved14[13];
|
||||
|
||||
u32 epu2apu_mb_offset;
|
||||
u32 epu2apu_irq;
|
||||
u32 epu2apu_irq_ack;
|
||||
u32 reserved15[13];
|
||||
u32 reserved16[8];
|
||||
|
||||
/* Fields for HPU: */
|
||||
|
||||
u32 hpu_state;
|
||||
u32 reserved21[7];
|
||||
u32 cpu2hpu_mb_offset;
|
||||
u32 cpu2hpu_irq;
|
||||
u32 cpu2hpu_irq_ack;
|
||||
u32 reserved22[13];
|
||||
|
||||
u32 apu2hpu_mb_offset;
|
||||
u32 apu2hpu_irq;
|
||||
u32 apu2hpu_irq_ack;
|
||||
u32 reserved23[13];
|
||||
|
||||
u32 ppu2hpu_mb_offset;
|
||||
u32 ppu2hpu_irq;
|
||||
u32 ppu2hpu_irq_ack;
|
||||
u32 reserved24[13];
|
||||
|
||||
u32 epu2hpu_mb_offset;
|
||||
u32 epu2hpu_irq;
|
||||
u32 epu2hpu_irq_ack;
|
||||
u32 reserved25[13];
|
||||
u32 reserved26[8];
|
||||
|
||||
/* Fields for PPU: */
|
||||
|
||||
u32 ppu_state;
|
||||
u32 reserved31[7];
|
||||
u32 cpu2ppu_mb_offset;
|
||||
u32 cpu2ppu_irq;
|
||||
u32 cpu2ppu_irq_ack;
|
||||
u32 reserved32[13];
|
||||
|
||||
u32 apu2ppu_mb_offset;
|
||||
u32 apu2ppu_irq;
|
||||
u32 apu2ppu_irq_ack;
|
||||
u32 reserved33[13];
|
||||
|
||||
u32 hpu2ppu_mb_offset;
|
||||
u32 hpu2ppu_irq;
|
||||
u32 hpu2ppu_irq_ack;
|
||||
u32 reserved34[13];
|
||||
|
||||
u32 epu2ppu_mb_offset;
|
||||
u32 epu2ppu_irq;
|
||||
u32 epu2ppu_irq_ack;
|
||||
u32 reserved35[13];
|
||||
u32 reserved36[8];
|
||||
|
||||
/* Fields for EPU: */
|
||||
|
||||
u32 epu_state;
|
||||
u32 reserved41[7];
|
||||
u32 cpu2epu_mb_offset;
|
||||
u32 cpu2epu_irq;
|
||||
u32 cpu2epu_irq_ack;
|
||||
u32 reserved42[13];
|
||||
|
||||
u32 apu2epu_mb_offset;
|
||||
u32 apu2epu_irq;
|
||||
u32 apu2epu_irq_ack;
|
||||
u32 reserved43[13];
|
||||
|
||||
u32 hpu2epu_mb_offset;
|
||||
u32 hpu2epu_irq;
|
||||
u32 hpu2epu_irq_ack;
|
||||
u32 reserved44[13];
|
||||
|
||||
u32 ppu2epu_mb_offset;
|
||||
u32 ppu2epu_irq;
|
||||
u32 ppu2epu_irq_ack;
|
||||
u32 reserved45[13];
|
||||
u32 reserved46[8];
|
||||
|
||||
u32 semaphores[8]; /* Semaphores */
|
||||
|
||||
u32 reserved50[32]; /* Reserved for future use */
|
||||
|
||||
struct cx18_mailbox apu2cpu_mb;
|
||||
struct cx18_mailbox hpu2cpu_mb;
|
||||
struct cx18_mailbox ppu2cpu_mb;
|
||||
struct cx18_mailbox epu2cpu_mb;
|
||||
|
||||
struct cx18_mailbox cpu2apu_mb;
|
||||
struct cx18_mailbox hpu2apu_mb;
|
||||
struct cx18_mailbox ppu2apu_mb;
|
||||
struct cx18_mailbox epu2apu_mb;
|
||||
|
||||
struct cx18_mailbox cpu2hpu_mb;
|
||||
struct cx18_mailbox apu2hpu_mb;
|
||||
struct cx18_mailbox ppu2hpu_mb;
|
||||
struct cx18_mailbox epu2hpu_mb;
|
||||
|
||||
struct cx18_mailbox cpu2ppu_mb;
|
||||
struct cx18_mailbox apu2ppu_mb;
|
||||
struct cx18_mailbox hpu2ppu_mb;
|
||||
struct cx18_mailbox epu2ppu_mb;
|
||||
|
||||
struct cx18_mailbox cpu2epu_mb;
|
||||
struct cx18_mailbox apu2epu_mb;
|
||||
struct cx18_mailbox hpu2epu_mb;
|
||||
struct cx18_mailbox ppu2epu_mb;
|
||||
|
||||
struct cx18_mdl_ack cpu_mdl_ack[CX18_MAX_STREAMS][2];
|
||||
struct cx18_mdl cpu_mdl[1];
|
||||
};
|
||||
|
||||
void cx18_init_scb(struct cx18 *cx);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,566 @@
|
|||
/*
|
||||
* cx18 init/start/stop/exit stream functions
|
||||
*
|
||||
* Derived from ivtv-streams.c
|
||||
*
|
||||
* Copyright (C) 2007 Hans Verkuil <hverkuil@xs4all.nl>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
* 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include "cx18-driver.h"
|
||||
#include "cx18-fileops.h"
|
||||
#include "cx18-mailbox.h"
|
||||
#include "cx18-i2c.h"
|
||||
#include "cx18-queue.h"
|
||||
#include "cx18-ioctl.h"
|
||||
#include "cx18-streams.h"
|
||||
#include "cx18-cards.h"
|
||||
#include "cx18-scb.h"
|
||||
#include "cx18-av-core.h"
|
||||
#include "cx18-dvb.h"
|
||||
|
||||
#define CX18_DSP0_INTERRUPT_MASK 0xd0004C
|
||||
|
||||
static struct file_operations cx18_v4l2_enc_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = cx18_v4l2_read,
|
||||
.open = cx18_v4l2_open,
|
||||
.ioctl = cx18_v4l2_ioctl,
|
||||
.release = cx18_v4l2_close,
|
||||
.poll = cx18_v4l2_enc_poll,
|
||||
};
|
||||
|
||||
/* offset from 0 to register ts v4l2 minors on */
|
||||
#define CX18_V4L2_ENC_TS_OFFSET 16
|
||||
/* offset from 0 to register pcm v4l2 minors on */
|
||||
#define CX18_V4L2_ENC_PCM_OFFSET 24
|
||||
/* offset from 0 to register yuv v4l2 minors on */
|
||||
#define CX18_V4L2_ENC_YUV_OFFSET 32
|
||||
|
||||
static struct {
|
||||
const char *name;
|
||||
int vfl_type;
|
||||
int minor_offset;
|
||||
int dma;
|
||||
enum v4l2_buf_type buf_type;
|
||||
struct file_operations *fops;
|
||||
} cx18_stream_info[] = {
|
||||
{ /* CX18_ENC_STREAM_TYPE_MPG */
|
||||
"encoder MPEG",
|
||||
VFL_TYPE_GRABBER, 0,
|
||||
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
||||
&cx18_v4l2_enc_fops
|
||||
},
|
||||
{ /* CX18_ENC_STREAM_TYPE_TS */
|
||||
"TS",
|
||||
VFL_TYPE_GRABBER, -1,
|
||||
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
||||
&cx18_v4l2_enc_fops
|
||||
},
|
||||
{ /* CX18_ENC_STREAM_TYPE_YUV */
|
||||
"encoder YUV",
|
||||
VFL_TYPE_GRABBER, CX18_V4L2_ENC_YUV_OFFSET,
|
||||
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
||||
&cx18_v4l2_enc_fops
|
||||
},
|
||||
{ /* CX18_ENC_STREAM_TYPE_VBI */
|
||||
"encoder VBI",
|
||||
VFL_TYPE_VBI, 0,
|
||||
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VBI_CAPTURE,
|
||||
&cx18_v4l2_enc_fops
|
||||
},
|
||||
{ /* CX18_ENC_STREAM_TYPE_PCM */
|
||||
"encoder PCM audio",
|
||||
VFL_TYPE_GRABBER, CX18_V4L2_ENC_PCM_OFFSET,
|
||||
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_PRIVATE,
|
||||
&cx18_v4l2_enc_fops
|
||||
},
|
||||
{ /* CX18_ENC_STREAM_TYPE_IDX */
|
||||
"encoder IDX",
|
||||
VFL_TYPE_GRABBER, -1,
|
||||
PCI_DMA_FROMDEVICE, V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
||||
&cx18_v4l2_enc_fops
|
||||
},
|
||||
{ /* CX18_ENC_STREAM_TYPE_RAD */
|
||||
"encoder radio",
|
||||
VFL_TYPE_RADIO, 0,
|
||||
PCI_DMA_NONE, V4L2_BUF_TYPE_PRIVATE,
|
||||
&cx18_v4l2_enc_fops
|
||||
},
|
||||
};
|
||||
|
||||
static void cx18_stream_init(struct cx18 *cx, int type)
|
||||
{
|
||||
struct cx18_stream *s = &cx->streams[type];
|
||||
struct video_device *dev = s->v4l2dev;
|
||||
u32 max_size = cx->options.megabytes[type] * 1024 * 1024;
|
||||
|
||||
/* we need to keep v4l2dev, so restore it afterwards */
|
||||
memset(s, 0, sizeof(*s));
|
||||
s->v4l2dev = dev;
|
||||
|
||||
/* initialize cx18_stream fields */
|
||||
s->cx = cx;
|
||||
s->type = type;
|
||||
s->name = cx18_stream_info[type].name;
|
||||
s->handle = 0xffffffff;
|
||||
|
||||
s->dma = cx18_stream_info[type].dma;
|
||||
s->buf_size = cx->stream_buf_size[type];
|
||||
if (s->buf_size)
|
||||
s->buffers = max_size / s->buf_size;
|
||||
if (s->buffers > 63) {
|
||||
/* Each stream has a maximum of 63 buffers,
|
||||
ensure we do not exceed that. */
|
||||
s->buffers = 63;
|
||||
s->buf_size = (max_size / s->buffers) & ~0xfff;
|
||||
}
|
||||
spin_lock_init(&s->qlock);
|
||||
init_waitqueue_head(&s->waitq);
|
||||
s->id = -1;
|
||||
cx18_queue_init(&s->q_free);
|
||||
cx18_queue_init(&s->q_full);
|
||||
cx18_queue_init(&s->q_io);
|
||||
}
|
||||
|
||||
static int cx18_prep_dev(struct cx18 *cx, int type)
|
||||
{
|
||||
struct cx18_stream *s = &cx->streams[type];
|
||||
u32 cap = cx->v4l2_cap;
|
||||
int minor_offset = cx18_stream_info[type].minor_offset;
|
||||
int minor;
|
||||
|
||||
/* These four fields are always initialized. If v4l2dev == NULL, then
|
||||
this stream is not in use. In that case no other fields but these
|
||||
four can be used. */
|
||||
s->v4l2dev = NULL;
|
||||
s->cx = cx;
|
||||
s->type = type;
|
||||
s->name = cx18_stream_info[type].name;
|
||||
|
||||
/* Check whether the radio is supported */
|
||||
if (type == CX18_ENC_STREAM_TYPE_RAD && !(cap & V4L2_CAP_RADIO))
|
||||
return 0;
|
||||
|
||||
/* Check whether VBI is supported */
|
||||
if (type == CX18_ENC_STREAM_TYPE_VBI &&
|
||||
!(cap & (V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)))
|
||||
return 0;
|
||||
|
||||
/* card number + user defined offset + device offset */
|
||||
minor = cx->num + cx18_first_minor + minor_offset;
|
||||
|
||||
/* User explicitly selected 0 buffers for these streams, so don't
|
||||
create them. */
|
||||
if (cx18_stream_info[type].dma != PCI_DMA_NONE &&
|
||||
cx->options.megabytes[type] == 0) {
|
||||
CX18_INFO("Disabled %s device\n", cx18_stream_info[type].name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
cx18_stream_init(cx, type);
|
||||
|
||||
if (minor_offset == -1)
|
||||
return 0;
|
||||
|
||||
/* allocate and initialize the v4l2 video device structure */
|
||||
s->v4l2dev = video_device_alloc();
|
||||
if (s->v4l2dev == NULL) {
|
||||
CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
|
||||
s->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
s->v4l2dev->type =
|
||||
VID_TYPE_CAPTURE | VID_TYPE_TUNER | VID_TYPE_TELETEXT |
|
||||
VID_TYPE_CLIPPING | VID_TYPE_SCALES | VID_TYPE_MPEG_ENCODER;
|
||||
snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18%d %s",
|
||||
cx->num, s->name);
|
||||
|
||||
s->v4l2dev->minor = minor;
|
||||
s->v4l2dev->dev = &cx->dev->dev;
|
||||
s->v4l2dev->fops = cx18_stream_info[type].fops;
|
||||
s->v4l2dev->release = video_device_release;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize v4l2 variables and register v4l2 devices */
|
||||
int cx18_streams_setup(struct cx18 *cx)
|
||||
{
|
||||
int type;
|
||||
|
||||
/* Setup V4L2 Devices */
|
||||
for (type = 0; type < CX18_MAX_STREAMS; type++) {
|
||||
/* Prepare device */
|
||||
if (cx18_prep_dev(cx, type))
|
||||
break;
|
||||
|
||||
/* Allocate Stream */
|
||||
if (cx18_stream_alloc(&cx->streams[type]))
|
||||
break;
|
||||
}
|
||||
if (type == CX18_MAX_STREAMS)
|
||||
return 0;
|
||||
|
||||
/* One or more streams could not be initialized. Clean 'em all up. */
|
||||
cx18_streams_cleanup(cx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int cx18_reg_dev(struct cx18 *cx, int type)
|
||||
{
|
||||
struct cx18_stream *s = &cx->streams[type];
|
||||
int vfl_type = cx18_stream_info[type].vfl_type;
|
||||
int minor;
|
||||
|
||||
/* TODO: Shouldn't this be a VFL_TYPE_TRANSPORT or something?
|
||||
* We need a VFL_TYPE_TS defined.
|
||||
*/
|
||||
if (strcmp("TS", s->name) == 0) {
|
||||
/* just return if no DVB is supported */
|
||||
if ((cx->card->hw_all & CX18_HW_DVB) == 0)
|
||||
return 0;
|
||||
if (cx18_dvb_register(s) < 0) {
|
||||
CX18_ERR("DVB failed to register\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
if (s->v4l2dev == NULL)
|
||||
return 0;
|
||||
|
||||
minor = s->v4l2dev->minor;
|
||||
|
||||
/* Register device. First try the desired minor, then any free one. */
|
||||
if (video_register_device(s->v4l2dev, vfl_type, minor) &&
|
||||
video_register_device(s->v4l2dev, vfl_type, -1)) {
|
||||
CX18_ERR("Couldn't register v4l2 device for %s minor %d\n",
|
||||
s->name, minor);
|
||||
video_device_release(s->v4l2dev);
|
||||
s->v4l2dev = NULL;
|
||||
return -ENOMEM;
|
||||
}
|
||||
minor = s->v4l2dev->minor;
|
||||
|
||||
switch (vfl_type) {
|
||||
case VFL_TYPE_GRABBER:
|
||||
CX18_INFO("Registered device video%d for %s (%d MB)\n",
|
||||
minor, s->name, cx->options.megabytes[type]);
|
||||
break;
|
||||
|
||||
case VFL_TYPE_RADIO:
|
||||
CX18_INFO("Registered device radio%d for %s\n",
|
||||
minor - MINOR_VFL_TYPE_RADIO_MIN, s->name);
|
||||
break;
|
||||
|
||||
case VFL_TYPE_VBI:
|
||||
if (cx->options.megabytes[type])
|
||||
CX18_INFO("Registered device vbi%d for %s (%d MB)\n",
|
||||
minor - MINOR_VFL_TYPE_VBI_MIN,
|
||||
s->name, cx->options.megabytes[type]);
|
||||
else
|
||||
CX18_INFO("Registered device vbi%d for %s\n",
|
||||
minor - MINOR_VFL_TYPE_VBI_MIN, s->name);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Register v4l2 devices */
|
||||
int cx18_streams_register(struct cx18 *cx)
|
||||
{
|
||||
int type;
|
||||
int err = 0;
|
||||
|
||||
/* Register V4L2 devices */
|
||||
for (type = 0; type < CX18_MAX_STREAMS; type++)
|
||||
err |= cx18_reg_dev(cx, type);
|
||||
|
||||
if (err == 0)
|
||||
return 0;
|
||||
|
||||
/* One or more streams could not be initialized. Clean 'em all up. */
|
||||
cx18_streams_cleanup(cx);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* Unregister v4l2 devices */
|
||||
void cx18_streams_cleanup(struct cx18 *cx)
|
||||
{
|
||||
struct video_device *vdev;
|
||||
int type;
|
||||
|
||||
/* Teardown all streams */
|
||||
for (type = 0; type < CX18_MAX_STREAMS; type++) {
|
||||
if (cx->streams[type].dvb.enabled)
|
||||
cx18_dvb_unregister(&cx->streams[type]);
|
||||
|
||||
vdev = cx->streams[type].v4l2dev;
|
||||
|
||||
cx->streams[type].v4l2dev = NULL;
|
||||
if (vdev == NULL)
|
||||
continue;
|
||||
|
||||
cx18_stream_free(&cx->streams[type]);
|
||||
|
||||
/* Unregister device */
|
||||
video_unregister_device(vdev);
|
||||
}
|
||||
}
|
||||
|
||||
static void cx18_vbi_setup(struct cx18_stream *s)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
int raw = cx->vbi.sliced_in->service_set == 0;
|
||||
u32 data[CX2341X_MBOX_MAX_DATA];
|
||||
int lines;
|
||||
|
||||
if (cx->is_60hz) {
|
||||
cx->vbi.count = 12;
|
||||
cx->vbi.start[0] = 10;
|
||||
cx->vbi.start[1] = 273;
|
||||
} else { /* PAL/SECAM */
|
||||
cx->vbi.count = 18;
|
||||
cx->vbi.start[0] = 6;
|
||||
cx->vbi.start[1] = 318;
|
||||
}
|
||||
|
||||
/* setup VBI registers */
|
||||
cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
|
||||
|
||||
/* determine number of lines and total number of VBI bytes.
|
||||
A raw line takes 1443 bytes: 2 * 720 + 4 byte frame header - 1
|
||||
The '- 1' byte is probably an unused U or V byte. Or something...
|
||||
A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
|
||||
header, 42 data bytes + checksum (to be confirmed) */
|
||||
if (raw) {
|
||||
lines = cx->vbi.count * 2;
|
||||
} else {
|
||||
lines = cx->is_60hz ? 24 : 38;
|
||||
if (cx->is_60hz)
|
||||
lines += 2;
|
||||
}
|
||||
|
||||
cx->vbi.enc_size = lines *
|
||||
(raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
|
||||
|
||||
data[0] = s->handle;
|
||||
/* Lines per field */
|
||||
data[1] = (lines / 2) | ((lines / 2) << 16);
|
||||
/* bytes per line */
|
||||
data[2] = (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
|
||||
/* Every X number of frames a VBI interrupt arrives
|
||||
(frames as in 25 or 30 fps) */
|
||||
data[3] = 1;
|
||||
/* Setup VBI for the cx25840 digitizer */
|
||||
if (raw) {
|
||||
data[4] = 0x20602060;
|
||||
data[5] = 0x30703070;
|
||||
} else {
|
||||
data[4] = 0xB0F0B0F0;
|
||||
data[5] = 0xA0E0A0E0;
|
||||
}
|
||||
|
||||
CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
|
||||
data[0], data[1], data[2], data[3], data[4], data[5]);
|
||||
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_VBI)
|
||||
cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
|
||||
}
|
||||
|
||||
int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
|
||||
{
|
||||
u32 data[MAX_MB_ARGUMENTS];
|
||||
struct cx18 *cx = s->cx;
|
||||
struct list_head *p;
|
||||
int ts = 0;
|
||||
int captype = 0;
|
||||
|
||||
if (s->v4l2dev == NULL && s->dvb.enabled == 0)
|
||||
return -EINVAL;
|
||||
|
||||
CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
|
||||
|
||||
switch (s->type) {
|
||||
case CX18_ENC_STREAM_TYPE_MPG:
|
||||
captype = CAPTURE_CHANNEL_TYPE_MPEG;
|
||||
cx->mpg_data_received = cx->vbi_data_inserted = 0;
|
||||
cx->dualwatch_jiffies = jiffies;
|
||||
cx->dualwatch_stereo_mode = cx->params.audio_properties & 0x300;
|
||||
cx->search_pack_header = 0;
|
||||
break;
|
||||
|
||||
case CX18_ENC_STREAM_TYPE_TS:
|
||||
captype = CAPTURE_CHANNEL_TYPE_TS;
|
||||
ts = 1;
|
||||
break;
|
||||
case CX18_ENC_STREAM_TYPE_YUV:
|
||||
captype = CAPTURE_CHANNEL_TYPE_YUV;
|
||||
break;
|
||||
case CX18_ENC_STREAM_TYPE_PCM:
|
||||
captype = CAPTURE_CHANNEL_TYPE_PCM;
|
||||
break;
|
||||
case CX18_ENC_STREAM_TYPE_VBI:
|
||||
captype = cx->vbi.sliced_in->service_set ?
|
||||
CAPTURE_CHANNEL_TYPE_SLICED_VBI : CAPTURE_CHANNEL_TYPE_VBI;
|
||||
cx->vbi.frame = 0;
|
||||
cx->vbi.inserted_frame = 0;
|
||||
memset(cx->vbi.sliced_mpeg_size,
|
||||
0, sizeof(cx->vbi.sliced_mpeg_size));
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
s->buffers_stolen = 0;
|
||||
|
||||
/* mute/unmute video */
|
||||
cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
|
||||
s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags));
|
||||
|
||||
/* Clear Streamoff flags in case left from last capture */
|
||||
clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
|
||||
|
||||
cx18_vapi_result(cx, data, CX18_CREATE_TASK, 1, CPU_CMD_MASK_CAPTURE);
|
||||
s->handle = data[0];
|
||||
cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
|
||||
|
||||
if (atomic_read(&cx->capturing) == 0 && !ts) {
|
||||
/* Stuff from Windows, we don't know what it is */
|
||||
cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
|
||||
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
|
||||
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
|
||||
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
|
||||
cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12);
|
||||
|
||||
cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
|
||||
s->handle, cx->digitizer, cx->digitizer);
|
||||
|
||||
/* Setup VBI */
|
||||
if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
|
||||
cx18_vbi_setup(s);
|
||||
|
||||
/* assign program index info.
|
||||
Mask 7: select I/P/B, Num_req: 400 max */
|
||||
cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
|
||||
|
||||
/* Setup API for Stream */
|
||||
cx2341x_update(cx, cx18_api_func, NULL, &cx->params);
|
||||
}
|
||||
|
||||
if (atomic_read(&cx->capturing) == 0) {
|
||||
clear_bit(CX18_F_I_EOS, &cx->i_flags);
|
||||
write_reg(7, CX18_DSP0_INTERRUPT_MASK);
|
||||
}
|
||||
|
||||
cx18_vapi(cx, CX18_CPU_DE_SET_MDL_ACK, 3, s->handle,
|
||||
(void *)&cx->scb->cpu_mdl_ack[s->type][0] - cx->enc_mem,
|
||||
(void *)&cx->scb->cpu_mdl_ack[s->type][1] - cx->enc_mem);
|
||||
|
||||
list_for_each(p, &s->q_free.list) {
|
||||
struct cx18_buffer *buf = list_entry(p, struct cx18_buffer, list);
|
||||
|
||||
writel(buf->dma_handle, &cx->scb->cpu_mdl[buf->id].paddr);
|
||||
writel(s->buf_size, &cx->scb->cpu_mdl[buf->id].length);
|
||||
cx18_vapi(cx, CX18_CPU_DE_SET_MDL, 5, s->handle,
|
||||
(void *)&cx->scb->cpu_mdl[buf->id] - cx->enc_mem, 1,
|
||||
buf->id, s->buf_size);
|
||||
}
|
||||
/* begin_capture */
|
||||
if (cx18_vapi(cx, CX18_CPU_CAPTURE_START, 1, s->handle)) {
|
||||
CX18_DEBUG_WARN("Error starting capture!\n");
|
||||
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* you're live! sit back and await interrupts :) */
|
||||
atomic_inc(&cx->capturing);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cx18_stop_all_captures(struct cx18 *cx)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
|
||||
struct cx18_stream *s = &cx->streams[i];
|
||||
|
||||
if (s->v4l2dev == NULL && s->dvb.enabled == 0)
|
||||
continue;
|
||||
if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
|
||||
cx18_stop_v4l2_encode_stream(s, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
|
||||
{
|
||||
struct cx18 *cx = s->cx;
|
||||
unsigned long then;
|
||||
|
||||
if (s->v4l2dev == NULL && s->dvb.enabled == 0)
|
||||
return -EINVAL;
|
||||
|
||||
/* This function assumes that you are allowed to stop the capture
|
||||
and that we are actually capturing */
|
||||
|
||||
CX18_DEBUG_INFO("Stop Capture\n");
|
||||
|
||||
if (atomic_read(&cx->capturing) == 0)
|
||||
return 0;
|
||||
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG)
|
||||
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 2, s->handle, !gop_end);
|
||||
else
|
||||
cx18_vapi(cx, CX18_CPU_CAPTURE_STOP, 1, s->handle);
|
||||
|
||||
then = jiffies;
|
||||
|
||||
if (s->type == CX18_ENC_STREAM_TYPE_MPG && gop_end) {
|
||||
CX18_INFO("ignoring gop_end: not (yet?) supported by the firmware\n");
|
||||
}
|
||||
|
||||
atomic_dec(&cx->capturing);
|
||||
|
||||
/* Clear capture and no-read bits */
|
||||
clear_bit(CX18_F_S_STREAMING, &s->s_flags);
|
||||
|
||||
cx18_vapi(cx, CX18_DESTROY_TASK, 1, s->handle);
|
||||
s->handle = 0xffffffff;
|
||||
|
||||
if (atomic_read(&cx->capturing) > 0)
|
||||
return 0;
|
||||
|
||||
write_reg(5, CX18_DSP0_INTERRUPT_MASK);
|
||||
wake_up(&s->waitq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
u32 cx18_find_handle(struct cx18 *cx)
|
||||
{
|
||||
int i;
|
||||
|
||||
/* find first available handle to be used for global settings */
|
||||
for (i = 0; i < CX18_MAX_STREAMS; i++) {
|
||||
struct cx18_stream *s = &cx->streams[i];
|
||||
|
||||
if (s->v4l2dev && s->handle)
|
||||
return s->handle;
|
||||
}
|
||||
return 0;
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче