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:
Linus Torvalds 2008-04-29 14:53:40 -07:00
commit 2d5e3e8d28
143 changed files with 11083 additions and 717 deletions

View File

@ -128,7 +128,7 @@
127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090] 127 -> Beholder BeholdTV 507 FM/RDS / BeholdTV 509 FM [0000:5071,0000:507B,5ace:5070,5ace:5090]
128 -> Beholder BeholdTV Columbus TVFM [0000:5201] 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] 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] 131 -> Twinhan Hybrid DTV-DVB 3056 PCI [1822:0022]
132 -> Genius TVGO AM11MCE 132 -> Genius TVGO AM11MCE
133 -> NXP Snake DVB-S reference design 133 -> NXP Snake DVB-S reference design
@ -140,3 +140,4 @@
139 -> Compro VideoMate T750 [185b:c900] 139 -> Compro VideoMate T750 [185b:c900]
140 -> Avermedia DVB-S Pro A700 [1461:a7a1] 140 -> Avermedia DVB-S Pro A700 [1461:a7a1]
141 -> Avermedia DVB-S Hybrid+FM A700 [1461:a7a2] 141 -> Avermedia DVB-S Hybrid+FM A700 [1461:a7a2]
142 -> Beholder BeholdTV H6 [5ace:6290]

View File

@ -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

View File

@ -5,16 +5,20 @@
menu "Multimedia devices" menu "Multimedia devices"
depends on HAS_IOMEM depends on HAS_IOMEM
comment "Multimedia core support"
#
# V4L core and enabled API's
#
config VIDEO_DEV config VIDEO_DEV
tristate "Video For Linux" tristate "Video For Linux"
---help--- ---help---
Support for audio/video capture and overlay devices and FM radio V4L core support for video capture and overlay devices, webcams and
cards. The exact capabilities of each device vary. AM/FM radio cards.
This kernel includes support for the new Video for Linux Two API, This kernel includes support for the new Video for Linux Two API,
(V4L2) as well as the original system. Drivers and applications (V4L2).
need to be rewritten to use V4L2, but drivers for popular cards
and applications for most video capture functions already exist.
Additional info and docs are available on the web at Additional info and docs are available on the web at
<http://linuxtv.org> <http://linuxtv.org>
@ -36,8 +40,11 @@ config VIDEO_ALLOW_V4L1
default VIDEO_DEV && VIDEO_V4L2_COMMON default VIDEO_DEV && VIDEO_V4L2_COMMON
select VIDEO_V4L1_COMPAT select VIDEO_V4L1_COMPAT
---help--- ---help---
Enables a compatibility API used by most V4L2 devices to allow Enables drivers based on the legacy V4L1 API.
its usage with legacy applications that supports only 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. If you are unsure as to whether this is required, answer Y.
@ -46,9 +53,8 @@ config VIDEO_V4L1_COMPAT
depends on VIDEO_DEV depends on VIDEO_DEV
default VIDEO_DEV default VIDEO_DEV
---help--- ---help---
This api were developed to be used at Kernel 2.2 and 2.4, but Enables a compatibility API used by most V4L2 devices to allow
lacks support for several video standards. There are several its usage with legacy applications that supports only V4L1 api.
drivers at kernel that still depends on it.
Documentation for the original API is included in the file Documentation for the original API is included in the file
<Documentation/video4linux/API.html>. <Documentation/video4linux/API.html>.
@ -58,136 +64,58 @@ config VIDEO_V4L1_COMPAT
If you are unsure as to whether this is required, answer Y. If you are unsure as to whether this is required, answer Y.
config VIDEO_V4L2 #
tristate # DVB Core
depends on VIDEO_DEV && VIDEO_V4L2_COMMON #
default VIDEO_DEV && VIDEO_V4L2_COMMON
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 tristate
depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 default DVB_CORE || VIDEO_DEV
default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 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/video/Kconfig"
source "drivers/media/radio/Kconfig" source "drivers/media/radio/Kconfig"
#
# DVB adapters
#
source "drivers/media/dvb/Kconfig" 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 config DAB
boolean "DAB adapters" boolean "DAB adapters"
---help--- ---help---

View File

@ -2,10 +2,10 @@
# Makefile for the kernel multimedia device drivers. # Makefile for the kernel multimedia device drivers.
# #
obj-y := common/ obj-$(CONFIG_VIDEO_MEDIA) += common/
obj-y += video/
# 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_VIDEO_DEV) += radio/
obj-$(CONFIG_DVB_CORE) += dvb/ obj-$(CONFIG_DVB_CORE) += dvb/
ifeq ($(CONFIG_DVB_CORE),)
obj-$(CONFIG_VIDEO_TUNER) += dvb/frontends/
endif

View File

@ -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

View File

@ -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

View File

@ -30,7 +30,7 @@ struct mt2060_config {
u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ 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); extern struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1);
#else #else
static inline struct dvb_frontend * mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1) 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__); printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL; return NULL;
} }
#endif // CONFIG_DVB_TUNER_MT2060 #endif // CONFIG_MEDIA_TUNER_MT2060
#endif #endif

View File

@ -20,7 +20,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include "dvb_frontend.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, extern struct dvb_frontend *microtune_attach(struct dvb_frontend *fe,
struct i2c_adapter* i2c_adap, struct i2c_adapter* i2c_adap,
u8 i2c_addr); u8 i2c_addr);

View File

@ -30,7 +30,7 @@ struct mt2131_config {
u8 clock_out; /* 0 = off, 1 = CLK/4, 2 = CLK/2, 3 = CLK/1 */ 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, extern struct dvb_frontend* mt2131_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c, struct i2c_adapter *i2c,
struct mt2131_config *cfg, 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__); printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL; return NULL;
} }
#endif /* CONFIG_DVB_TUNER_MT2131 */ #endif /* CONFIG_MEDIA_TUNER_MT2131 */
#endif /* __MT2131_H__ */ #endif /* __MT2131_H__ */

View File

@ -24,7 +24,7 @@ struct mt2266_config {
u8 i2c_address; 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); extern struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg);
#else #else
static inline struct dvb_frontend * mt2266_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2266_config *cfg) 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__); printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL; return NULL;
} }
#endif // CONFIG_DVB_TUNER_MT2266 #endif // CONFIG_MEDIA_TUNER_MT2266
#endif #endif

View File

@ -36,7 +36,7 @@ struct qt1010_config {
* @param cfg tuner hw based configuration * @param cfg tuner hw based configuration
* @return fe pointer on success, NULL on failure * @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, extern struct dvb_frontend *qt1010_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c, struct i2c_adapter *i2c,
struct qt1010_config *cfg); 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__); printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL; return NULL;
} }
#endif // CONFIG_DVB_TUNER_QT1010 #endif // CONFIG_MEDIA_TUNER_QT1010
#endif #endif

View File

@ -81,7 +81,7 @@ struct tda18271_config {
unsigned int small_i2c:1; 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, extern struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
struct i2c_adapter *i2c, struct i2c_adapter *i2c,
struct tda18271_config *cfg); struct tda18271_config *cfg);

View File

@ -51,7 +51,7 @@ struct tda827x_config
* @param cfg optional callback function pointers. * @param cfg optional callback function pointers.
* @return FE pointer on success, NULL on failure. * @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, extern struct dvb_frontend* tda827x_attach(struct dvb_frontend *fe, int addr,
struct i2c_adapter *i2c, struct i2c_adapter *i2c,
struct tda827x_config *cfg); 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__); printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL; return NULL;
} }
#endif // CONFIG_DVB_TDA827X #endif // CONFIG_MEDIA_TUNER_TDA827X
#endif // __DVB_TDA827X_H__ #endif // __DVB_TDA827X_H__

View File

@ -578,16 +578,16 @@ static int tda829x_find_tuner(struct dvb_frontend *fe)
if ((data == 0x83) || (data == 0x84)) { if ((data == 0x83) || (data == 0x84)) {
priv->ver |= TDA18271; priv->ver |= TDA18271;
tda18271_attach(fe, priv->tda827x_addr, dvb_attach(tda18271_attach, fe, priv->tda827x_addr,
priv->i2c_props.adap, priv->i2c_props.adap, &tda829x_tda18271_config);
&tda829x_tda18271_config);
} else { } else {
if ((data & 0x3c) == 0) if ((data & 0x3c) == 0)
priv->ver |= TDA8275; priv->ver |= TDA8275;
else else
priv->ver |= TDA8275A; 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; priv->cfg.switch_addr = priv->i2c_props.addr;
} }
if (fe->ops.tuner_ops.init) if (fe->ops.tuner_ops.init)

View File

@ -29,7 +29,7 @@ struct tda829x_config {
#define TDA829X_DONT_PROBE 1 #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 int tda829x_probe(struct i2c_adapter *i2c_adap, u8 i2c_addr);
extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe, extern struct dvb_frontend *tda829x_attach(struct dvb_frontend *fe,

View File

@ -21,7 +21,7 @@
#include "dvb_frontend.h" #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, extern struct dvb_frontend *tda9887_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c_adap, struct i2c_adapter *i2c_adap,
u8 i2c_addr); u8 i2c_addr);

View File

@ -20,7 +20,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include "dvb_frontend.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 int tea5761_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe, extern struct dvb_frontend *tea5761_attach(struct dvb_frontend *fe,

View File

@ -39,7 +39,7 @@ struct tea5767_ctrl {
enum tea5767_xtal xtal_freq; 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 int tea5767_autodetection(struct i2c_adapter* i2c_adap, u8 i2c_addr);
extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe, extern struct dvb_frontend *tea5767_attach(struct dvb_frontend *fe,

View File

@ -20,7 +20,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include "dvb_frontend.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, extern struct dvb_frontend *simple_tuner_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c_adap, struct i2c_adapter *i2c_adap,
u8 i2c_addr, u8 i2c_addr,

View File

@ -47,7 +47,7 @@ struct xc2028_config {
#define XC2028_TUNER_RESET 0 #define XC2028_TUNER_RESET 0
#define XC2028_RESET_CLK 1 #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, extern struct dvb_frontend *xc2028_attach(struct dvb_frontend *fe,
struct xc2028_config *cfg); struct xc2028_config *cfg);
#else #else

View File

@ -45,8 +45,8 @@ struct xc5000_config {
/* xc5000 callback command */ /* xc5000 callback command */
#define XC5000_TUNER_RESET 0 #define XC5000_TUNER_RESET 0
#if defined(CONFIG_DVB_TUNER_XC5000) || \ #if defined(CONFIG_MEDIA_TUNER_XC5000) || \
(defined(CONFIG_DVB_TUNER_XC5000_MODULE) && defined(MODULE)) (defined(CONFIG_MEDIA_TUNER_XC5000_MODULE) && defined(MODULE))
extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe, extern struct dvb_frontend* xc5000_attach(struct dvb_frontend *fe,
struct i2c_adapter *i2c, struct i2c_adapter *i2c,
struct xc5000_config *cfg); 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__); printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
return NULL; return NULL;
} }
#endif // CONFIG_DVB_TUNER_XC5000 #endif // CONFIG_MEDIA_TUNER_XC5000
#endif // __XC5000_H__ #endif // __XC5000_H__

View File

@ -1,9 +1,7 @@
# #
# Multimedia device configuration # DVB device configuration
# #
source "drivers/media/dvb/dvb-core/Kconfig"
menuconfig DVB_CAPTURE_DRIVERS menuconfig DVB_CAPTURE_DRIVERS
bool "DVB/ATSC adapters" bool "DVB/ATSC adapters"
depends on DVB_CORE depends on DVB_CORE

View File

@ -9,7 +9,7 @@ config DVB_B2C2_FLEXCOP
select DVB_STV0297 if !DVB_FE_CUSTOMISE select DVB_STV0297 if !DVB_FE_CUSTOMISE
select DVB_BCM3510 if !DVB_FE_CUSTOMISE select DVB_BCM3510 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X 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_S5H1420 if !DVB_FE_CUSTOMISE
select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE
select DVB_ISL6421 if !DVB_FE_CUSTOMISE select DVB_ISL6421 if !DVB_FE_CUSTOMISE

View File

@ -14,4 +14,4 @@ b2c2-flexcop-usb-objs = flexcop-usb.o
obj-$(CONFIG_DVB_B2C2_FLEXCOP_USB) += b2c2-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/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
EXTRA_CFLAGS += -Idrivers/media/video/ EXTRA_CFLAGS += -Idrivers/media/common/tuners/

View File

@ -8,7 +8,7 @@ config DVB_BT8XX
select DVB_OR51211 if !DVB_FE_CUSTOMISE select DVB_OR51211 if !DVB_FE_CUSTOMISE
select DVB_LGDT330X if !DVB_FE_CUSTOMISE select DVB_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_ZL10353 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 select FW_LOADER
help help
Support for PCI cards based on the Bt8xx PCI bridge. Examples are Support for PCI cards based on the Bt8xx PCI bridge. Examples are

View File

@ -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/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += -Idrivers/media/video/bt8xx EXTRA_CFLAGS += -Idrivers/media/video/bt8xx
EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/common/tuners

View File

@ -1714,7 +1714,7 @@ static void dst_release(struct dvb_frontend *fe)
struct dst_state *state = fe->demodulator_priv; struct dst_state *state = fe->demodulator_priv;
if (state->dst_ca) { if (state->dst_ca) {
dvb_unregister_device(state->dst_ca); dvb_unregister_device(state->dst_ca);
#ifdef CONFIG_DVB_CORE_ATTACH #ifdef CONFIG_MEDIA_ATTACH
symbol_put(dst_ca_attach); symbol_put(dst_ca_attach);
#endif #endif
} }

View File

@ -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.

View File

@ -1189,7 +1189,7 @@ int dvb_unregister_frontend(struct dvb_frontend* fe)
} }
EXPORT_SYMBOL(dvb_unregister_frontend); EXPORT_SYMBOL(dvb_unregister_frontend);
#ifdef CONFIG_DVB_CORE_ATTACH #ifdef CONFIG_MEDIA_ATTACH
void dvb_frontend_detach(struct dvb_frontend* fe) void dvb_frontend_detach(struct dvb_frontend* fe)
{ {
void *ptr; void *ptr;

View File

@ -115,7 +115,7 @@ extern int dvb_usercopy(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)); unsigned int cmd, void *arg));
/** generic DVB attach function. */ /** generic DVB attach function. */
#ifdef CONFIG_DVB_CORE_ATTACH #ifdef CONFIG_MEDIA_ATTACH
#define dvb_attach(FUNCTION, ARGS...) ({ \ #define dvb_attach(FUNCTION, ARGS...) ({ \
void *__r = NULL; \ void *__r = NULL; \
typeof(&FUNCTION) __a = symbol_request(FUNCTION); \ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \

View File

@ -25,7 +25,7 @@ config DVB_USB_A800
tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)" tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
depends on DVB_USB depends on DVB_USB
select DVB_DIB3000MC 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 select DVB_PLL if !DVB_FE_CUSTOMISE
help help
Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver. 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 depends on DVB_USB
select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE
select DVB_DIB3000MB select DVB_DIB3000MB
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help help
Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by 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. 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)" tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
depends on DVB_USB depends on DVB_USB
select DVB_DIB3000MC select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help help
Support for USB2.0 DVB-T receivers based on reference designs made by 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. 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_DIB7000P
select DVB_DIB7000M select DVB_DIB7000M
select DVB_DIB3000MC select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
select DVB_TUNER_MT2266 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2266 if !DVB_FE_CUSTOMISE
select DVB_TUNER_DIB0070 select DVB_TUNER_DIB0070
help help
Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The 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 depends on DVB_USB
select DVB_PLL if !DVB_FE_CUSTOMISE select DVB_PLL if !DVB_FE_CUSTOMISE
select DVB_DIB3000MC select DVB_DIB3000MC
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
help help
Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver. 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_LGDT330X if !DVB_FE_CUSTOMISE
select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_ZL10353 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 help
Say Y here to support the Conexant USB2.0 hybrid reference design. Say Y here to support the Conexant USB2.0 hybrid reference design.
Currently, only DVB and ATSC modes are supported, analog mode 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" tristate "Uli m920x DVB-T USB2.0 support"
depends on DVB_USB depends on DVB_USB
select DVB_MT352 if !DVB_FE_CUSTOMISE select DVB_MT352 if !DVB_FE_CUSTOMISE
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help help
Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver. Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
Currently, only devices with a product id of Currently, only devices with a product id of
@ -129,7 +129,7 @@ config DVB_USB_GL861
tristate "Genesys Logic GL861 USB2.0 support" tristate "Genesys Logic GL861 USB2.0 support"
depends on DVB_USB depends on DVB_USB
select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help help
Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0 Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
receiver with USB ID 0db0:5581. receiver with USB ID 0db0:5581.
@ -138,7 +138,7 @@ config DVB_USB_AU6610
tristate "Alcor Micro AU6610 USB2.0 support" tristate "Alcor Micro AU6610 USB2.0 support"
depends on DVB_USB depends on DVB_USB
select DVB_ZL10353 if !DVB_FE_CUSTOMISE select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help help
Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver. 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" tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
depends on DVB_USB depends on DVB_USB
select DVB_DIB3000MC 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 select DVB_PLL if !DVB_FE_CUSTOMISE
help help
Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver. 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 config DVB_USB_AF9005
tristate "Afatech AF9005 DVB-T USB1.1 support" tristate "Afatech AF9005 DVB-T USB1.1 support"
depends on DVB_USB && EXPERIMENTAL depends on DVB_USB && EXPERIMENTAL
select DVB_TUNER_MT2060 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_MT2060 if !DVB_FE_CUSTOMISE
select DVB_TUNER_QT1010 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_QT1010 if !DVB_FE_CUSTOMISE
help help
Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
and the TerraTec Cinergy T USB XE (Rev.1) and the TerraTec Cinergy T USB XE (Rev.1)

View File

@ -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/ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
# due to tuner-xc3028 # due to tuner-xc3028
EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/common/tuners

View File

@ -15,13 +15,6 @@ config DVB_FE_CUSTOMISE
comment "DVB-S (satellite) frontends" comment "DVB-S (satellite) frontends"
depends on DVB_CORE 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 config DVB_CX24110
tristate "Conexant CX24110 based" tristate "Conexant CX24110 based"
depends on DVB_CORE && I2C depends on DVB_CORE && I2C
@ -36,13 +29,6 @@ config DVB_CX24123
help help
A DVB-S tuner module. Say Y when you want to support this frontend. 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 config DVB_MT312
tristate "Zarlink VP310/MT312 based" tristate "Zarlink VP310/MT312 based"
depends on DVB_CORE && I2C depends on DVB_CORE && I2C
@ -50,13 +36,6 @@ config DVB_MT312
help help
A DVB-S tuner module. Say Y when you want to support this frontend. 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 config DVB_S5H1420
tristate "Samsung S5H1420 based" tristate "Samsung S5H1420 based"
depends on DVB_CORE && I2C depends on DVB_CORE && I2C
@ -64,6 +43,20 @@ config DVB_S5H1420
help help
A DVB-S tuner module. Say Y when you want to support this frontend. 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 config DVB_TDA10086
tristate "Philips TDA10086 based" tristate "Philips TDA10086 based"
depends on DVB_CORE && I2C depends on DVB_CORE && I2C
@ -71,6 +64,34 @@ config DVB_TDA10086
help help
A DVB-S tuner module. Say Y when you want to support this frontend. 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" comment "DVB-T (terrestrial) frontends"
depends on DVB_CORE depends on DVB_CORE
@ -315,7 +336,7 @@ config DVB_S5H1411
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend. to support this frontend.
comment "Tuners/PLL support" comment "Digital terrestrial only tuners/PLL"
depends on DVB_CORE depends on DVB_CORE
config DVB_PLL config DVB_PLL
@ -326,55 +347,6 @@ config DVB_PLL
This module drives a number of tuners based on PLL chips with a 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. 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 config DVB_TUNER_DIB0070
tristate "DiBcom DiB0070 silicon base-band tuner" tristate "DiBcom DiB0070 silicon base-band tuner"
depends on I2C depends on I2C
@ -384,21 +356,7 @@ config DVB_TUNER_DIB0070
This device is only used inside a SiP called togther with a This device is only used inside a SiP called togther with a
demodulator for now. demodulator for now.
config DVB_TUNER_XC5000 comment "SEC control devices for DVB-S"
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"
depends on DVB_CORE depends on DVB_CORE
config DVB_LNBP21 config DVB_LNBP21
@ -422,11 +380,4 @@ config DVB_ISL6421
help help
An SEC control chip. 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 endmenu

View File

@ -3,9 +3,7 @@
# #
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/
EXTRA_CFLAGS += -Idrivers/media/video/ EXTRA_CFLAGS += -Idrivers/media/common/tuners/
tda18271-objs := tda18271-tables.o tda18271-common.o tda18271-fe.o
obj-$(CONFIG_DVB_PLL) += dvb-pll.o obj-$(CONFIG_DVB_PLL) += dvb-pll.o
obj-$(CONFIG_DVB_STV0299) += stv0299.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_ISL6421) += isl6421.o
obj-$(CONFIG_DVB_TDA10086) += tda10086.o obj-$(CONFIG_DVB_TDA10086) += tda10086.o
obj-$(CONFIG_DVB_TDA826X) += tda826x.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_DIB0070) += dib0070.o
obj-$(CONFIG_DVB_TUNER_QT1010) += qt1010.o
obj-$(CONFIG_DVB_TUA6100) += tua6100.o obj-$(CONFIG_DVB_TUA6100) += tua6100.o
obj-$(CONFIG_DVB_TUNER_MT2131) += mt2131.o
obj-$(CONFIG_DVB_S5H1409) += s5h1409.o obj-$(CONFIG_DVB_S5H1409) += s5h1409.o
obj-$(CONFIG_DVB_TUNER_XC5000) += xc5000.o
obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o obj-$(CONFIG_DVB_TUNER_ITD1000) += itd1000.o
obj-$(CONFIG_DVB_AU8522) += au8522.o obj-$(CONFIG_DVB_AU8522) += au8522.o
obj-$(CONFIG_DVB_TDA10048) += tda10048.o obj-$(CONFIG_DVB_TDA10048) += tda10048.o

View File

@ -481,7 +481,7 @@ static void s5h1420_setsymbolrate(struct s5h1420_state* state,
val *= 2; val *= 2;
do_div(val, (state->fclk / 1000)); 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); v = s5h1420_readreg(state, Loop01);
s5h1420_writereg(state, Loop01, v & 0x7f); s5h1420_writereg(state, Loop01, v & 0x7f);

View File

@ -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 # Multimedia Video device configuration
# #
@ -644,7 +690,7 @@ config VIDEO_MXB
tristate "Siemens-Nixdorf 'Multimedia eXtension Board'" tristate "Siemens-Nixdorf 'Multimedia eXtension Board'"
depends on PCI && VIDEO_V4L1 && I2C depends on PCI && VIDEO_V4L1 && I2C
select VIDEO_SAA7146_VV select VIDEO_SAA7146_VV
select VIDEO_TUNER select MEDIA_TUNER
select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TEA6415C 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/ivtv/Kconfig"
source "drivers/media/video/cx18/Kconfig"
config VIDEO_M32R_AR config VIDEO_M32R_AR
tristate "AR devices" tristate "AR devices"
depends on M32R && VIDEO_V4L1 depends on M32R && VIDEO_V4L1

View File

@ -84,17 +84,7 @@ obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
obj-$(CONFIG_VIDEO_DPC) += dpc7146.o obj-$(CONFIG_VIDEO_DPC) += dpc7146.o
obj-$(CONFIG_TUNER_3036) += tuner-3036.o obj-$(CONFIG_TUNER_3036) += tuner-3036.o
obj-$(CONFIG_VIDEO_TUNER) += tuner.o obj-$(CONFIG_MEDIA_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_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o
obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.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_USB_QUICKCAM_MESSENGER) += usbvideo/
obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_IVTV) += ivtv/
obj-$(CONFIG_VIDEO_CX18) += cx18/
obj-$(CONFIG_VIDEO_VIVI) += vivi.o obj-$(CONFIG_VIDEO_VIVI) += vivi.o
obj-$(CONFIG_VIDEO_CX23885) += cx23885/ 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/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
EXTRA_CFLAGS += -Idrivers/media/common/tuners

View File

@ -4,7 +4,7 @@ config VIDEO_AU0828
depends on VIDEO_DEV && I2C && INPUT && DVB_CORE depends on VIDEO_DEV && I2C && INPUT && DVB_CORE
select I2C_ALGOBIT select I2C_ALGOBIT
select DVB_AU8522 if !DVB_FE_CUSTOMIZE select DVB_AU8522 if !DVB_FE_CUSTOMIZE
select DVB_TUNER_XC5000 if !DVB_FE_CUSTOMIZE select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
---help--- ---help---
This is a video4linux driver for Auvitek's USB device. This is a video4linux driver for Auvitek's USB device.

View File

@ -2,7 +2,7 @@ au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o
obj-$(CONFIG_VIDEO_AU0828) += au0828.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/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends EXTRA_CFLAGS += -Idrivers/media/dvb/frontends

View File

@ -6,7 +6,7 @@ config VIDEO_BT848
select VIDEO_BTCX select VIDEO_BTCX
select VIDEOBUF_DMA_SG select VIDEOBUF_DMA_SG
select VIDEO_IR select VIDEO_IR
select VIDEO_TUNER select MEDIA_TUNER
select VIDEO_TVEEPROM select VIDEO_TVEEPROM
select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO

View File

@ -9,4 +9,5 @@ bttv-objs := bttv-driver.o bttv-cards.o bttv-if.o \
obj-$(CONFIG_VIDEO_BT848) += bttv.o obj-$(CONFIG_VIDEO_BT848) += bttv.o
EXTRA_CFLAGS += -Idrivers/media/video EXTRA_CFLAGS += -Idrivers/media/video
EXTRA_CFLAGS += -Idrivers/media/common/tuners
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core

View File

@ -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.

View File

@ -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

View File

@ -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]);
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);

View File

@ -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 */

View File

@ -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;
}

View File

@ -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);

View File

@ -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");
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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
*/

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}

Some files were not shown because too many files have changed in this diff Show More