summaryrefslogtreecommitdiffstats
path: root/drivers/media/video/pvrusb2
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2008-04-24 11:21:08 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2008-04-24 11:21:08 -0700
commitc328d54cd4ad120d76284e46dcca6c6cf996154a (patch)
tree104c023be66faa5fce6e0a56c0a6d13c62fd21e5 /drivers/media/video/pvrusb2
parent346ad4b7fe392571f19314f153db9151dbc1d82b (diff)
parentb0166ab3a6ae6d7af8d9a21a7836154963c69a11 (diff)
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: (452 commits) V4L/DVB (7731): tuner-xc2028: fix signal strength calculus V4L/DVB (7730): tuner-xc2028: Fix SCODE load for MTS firmwares V4L/DVB (7729): Fix VIDIOCGAP corruption in ivtv V4L/DVB (7728): tea5761: bugzilla #10462: tea5761 autodetection code were broken V4L/DVB (7726): cx23885: Enable cx23417 support on the HVR1800 V4L/DVB (7725): cx23885: Add generic cx23417 hardware encoder support V4L/DVB (7723): pvrusb2: Clean up input selection list generation in V4L interface V4L/DVB (7722): pvrusb2: Implement FM radio support for Gotview USB2.0 DVD 2 V4L/DVB (7721): pvrusb2: Restructure cx23416 firmware loading to have a common exit point V4L/DVB (7720): pvrusb2: Fix bad error code on cx23416 firmware load failure V4L/DVB (7719): pvrusb2: Implement input selection enforcement V4L/DVB (7718): pvrusb2-dvb: update Kbuild selections V4L/DVB (7717): pvrusb2-dvb: add DVB-T support for Hauppauge pvrusb2 model 73xxx V4L/DVB (7716): pvrusb2: clean up global functions V4L/DVB (7715): pvrusb2: Clean out all use of __FUNCTION__ V4L/DVB (7714): pvrusb2: Fix hang on module removal V4L/DVB (7713): pvrusb2: Implement cleaner DVB kernel thread shutdown V4L/DVB (7712): pvrusb2: Close connect/disconnect race V4L/DVB (7711): pvrusb2: Fix race on module unload V4L/DVB (7710): pvrusb2: Implement critical digital streaming quirk for onair devices ...
Diffstat (limited to 'drivers/media/video/pvrusb2')
-rw-r--r--drivers/media/video/pvrusb2/Kconfig24
-rw-r--r--drivers/media/video/pvrusb2/Makefile6
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.c306
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.h16
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.c19
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c4
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.c24
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.c270
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-devattr.h72
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-dvb.c425
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-dvb.h41
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c19
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h43
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h26
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c903
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h39
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.c30
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.h12
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c16
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.c9
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c53
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c195
24 files changed, 2162 insertions, 394 deletions
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
index 6fc1b8be1a1..a8da90f69dd 100644
--- a/drivers/media/video/pvrusb2/Kconfig
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -58,6 +58,30 @@ config VIDEO_PVRUSB2_SYSFS
Note: This feature is experimental and subject to change.
+config VIDEO_PVRUSB2_DVB
+ bool "pvrusb2 DVB support (EXPERIMENTAL)"
+ default n
+ depends on VIDEO_PVRUSB2 && DVB_CORE && EXPERIMENTAL
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_S5H1409 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
+ select DVB_TDA18271 if !DVB_FE_CUSTOMIZE
+ select TUNER_SIMPLE if !DVB_FE_CUSTOMISE
+ select TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+ ---help---
+
+ This option enables compilation of a DVB interface for the
+ pvrusb2 driver. Currently this is very very experimental.
+ It is also limiting - the DVB interface can only access the
+ digital side of hybrid devices, and there are going to be
+ issues if you attempt to mess with the V4L side at the same
+ time. Don't turn this on unless you know what you are
+ doing.
+
+ If you are in doubt, say N.
+
+ Note: This feature is very experimental and might break
+
config VIDEO_PVRUSB2_DEBUGIFC
bool "pvrusb2 debug interface"
depends on VIDEO_PVRUSB2_SYSFS
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
index 47284e55864..5b3083c89aa 100644
--- a/drivers/media/video/pvrusb2/Makefile
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -1,5 +1,6 @@
obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
+obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o
pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \
@@ -9,6 +10,11 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
+ $(obj-pvrusb2-dvb-y) \
$(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
+EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c
index 160437b21e6..b5db6a5bab3 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -23,39 +23,193 @@
#include "pvrusb2-ioread.h"
#include "pvrusb2-hdw.h"
#include "pvrusb2-debug.h"
+#include <linux/wait.h>
+#include <linux/kthread.h>
#include <linux/errno.h>
#include <linux/string.h>
#include <linux/slab.h>
+static struct pvr2_context *pvr2_context_exist_first;
+static struct pvr2_context *pvr2_context_exist_last;
+static struct pvr2_context *pvr2_context_notify_first;
+static struct pvr2_context *pvr2_context_notify_last;
+static DEFINE_MUTEX(pvr2_context_mutex);
+static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_sync_data);
+static DECLARE_WAIT_QUEUE_HEAD(pvr2_context_cleanup_data);
+static int pvr2_context_cleanup_flag;
+static int pvr2_context_cleaned_flag;
+static struct task_struct *pvr2_context_thread_ptr;
+
+
+static void pvr2_context_set_notify(struct pvr2_context *mp, int fl)
+{
+ int signal_flag = 0;
+ mutex_lock(&pvr2_context_mutex);
+ if (fl) {
+ if (!mp->notify_flag) {
+ signal_flag = (pvr2_context_notify_first == NULL);
+ mp->notify_prev = pvr2_context_notify_last;
+ mp->notify_next = NULL;
+ pvr2_context_notify_last = mp;
+ if (mp->notify_prev) {
+ mp->notify_prev->notify_next = mp;
+ } else {
+ pvr2_context_notify_first = mp;
+ }
+ mp->notify_flag = !0;
+ }
+ } else {
+ if (mp->notify_flag) {
+ mp->notify_flag = 0;
+ if (mp->notify_next) {
+ mp->notify_next->notify_prev = mp->notify_prev;
+ } else {
+ pvr2_context_notify_last = mp->notify_prev;
+ }
+ if (mp->notify_prev) {
+ mp->notify_prev->notify_next = mp->notify_next;
+ } else {
+ pvr2_context_notify_first = mp->notify_next;
+ }
+ }
+ }
+ mutex_unlock(&pvr2_context_mutex);
+ if (signal_flag) wake_up(&pvr2_context_sync_data);
+}
+
static void pvr2_context_destroy(struct pvr2_context *mp)
{
- pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp);
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (destroy)",mp);
if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
+ pvr2_context_set_notify(mp, 0);
+ mutex_lock(&pvr2_context_mutex);
+ if (mp->exist_next) {
+ mp->exist_next->exist_prev = mp->exist_prev;
+ } else {
+ pvr2_context_exist_last = mp->exist_prev;
+ }
+ if (mp->exist_prev) {
+ mp->exist_prev->exist_next = mp->exist_next;
+ } else {
+ pvr2_context_exist_first = mp->exist_next;
+ }
+ if (!pvr2_context_exist_first) {
+ /* Trigger wakeup on control thread in case it is waiting
+ for an exit condition. */
+ wake_up(&pvr2_context_sync_data);
+ }
+ mutex_unlock(&pvr2_context_mutex);
kfree(mp);
}
-static void pvr2_context_state_check(struct pvr2_context *mp)
+static void pvr2_context_notify(struct pvr2_context *mp)
{
- if (mp->init_flag) return;
+ pvr2_context_set_notify(mp,!0);
+}
+
- switch (pvr2_hdw_get_state(mp->hdw)) {
- case PVR2_STATE_WARM: break;
- case PVR2_STATE_ERROR: break;
- case PVR2_STATE_READY: break;
- case PVR2_STATE_RUN: break;
- default: return;
+static void pvr2_context_check(struct pvr2_context *mp)
+{
+ struct pvr2_channel *ch1, *ch2;
+ pvr2_trace(PVR2_TRACE_CTXT,
+ "pvr2_context %p (notify)", mp);
+ if (!mp->initialized_flag && !mp->disconnect_flag) {
+ mp->initialized_flag = !0;
+ pvr2_trace(PVR2_TRACE_CTXT,
+ "pvr2_context %p (initialize)", mp);
+ /* Finish hardware initialization */
+ if (pvr2_hdw_initialize(mp->hdw,
+ (void (*)(void *))pvr2_context_notify,
+ mp)) {
+ mp->video_stream.stream =
+ pvr2_hdw_get_video_stream(mp->hdw);
+ /* Trigger interface initialization. By doing this
+ here initialization runs in our own safe and
+ cozy thread context. */
+ if (mp->setup_func) mp->setup_func(mp);
+ } else {
+ pvr2_trace(PVR2_TRACE_CTXT,
+ "pvr2_context %p (thread skipping setup)",
+ mp);
+ /* Even though initialization did not succeed,
+ we're still going to continue anyway. We need
+ to do this in order to await the expected
+ disconnect (which we will detect in the normal
+ course of operation). */
+ }
}
- pvr2_context_enter(mp); do {
- mp->init_flag = !0;
- mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw);
- if (mp->setup_func) {
- mp->setup_func(mp);
+ for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
+ ch2 = ch1->mc_next;
+ if (ch1->check_func) ch1->check_func(ch1);
+ }
+
+ if (mp->disconnect_flag && !mp->mc_first) {
+ /* Go away... */
+ pvr2_context_destroy(mp);
+ return;
+ }
+}
+
+
+static int pvr2_context_shutok(void)
+{
+ return pvr2_context_cleanup_flag && (pvr2_context_exist_first == NULL);
+}
+
+
+static int pvr2_context_thread_func(void *foo)
+{
+ struct pvr2_context *mp;
+
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread start");
+
+ do {
+ while ((mp = pvr2_context_notify_first) != NULL) {
+ pvr2_context_set_notify(mp, 0);
+ pvr2_context_check(mp);
}
- } while (0); pvr2_context_exit(mp);
- }
+ wait_event_interruptible(
+ pvr2_context_sync_data,
+ ((pvr2_context_notify_first != NULL) ||
+ pvr2_context_shutok()));
+ } while (!pvr2_context_shutok());
+
+ pvr2_context_cleaned_flag = !0;
+ wake_up(&pvr2_context_cleanup_data);
+
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread cleaned up");
+
+ wait_event_interruptible(
+ pvr2_context_sync_data,
+ kthread_should_stop());
+
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context thread end");
+
+ return 0;
+}
+
+
+int pvr2_context_global_init(void)
+{
+ pvr2_context_thread_ptr = kthread_run(pvr2_context_thread_func,
+ 0,
+ "pvrusb2-context");
+ return (pvr2_context_thread_ptr ? 0 : -ENOMEM);
+}
+
+
+void pvr2_context_global_done(void)
+{
+ pvr2_context_cleanup_flag = !0;
+ wake_up(&pvr2_context_sync_data);
+ wait_event_interruptible(
+ pvr2_context_cleanup_data,
+ pvr2_context_cleaned_flag);
+ kthread_stop(pvr2_context_thread_ptr);
+}
struct pvr2_context *pvr2_context_create(
@@ -66,67 +220,75 @@ struct pvr2_context *pvr2_context_create(
struct pvr2_context *mp = NULL;
mp = kzalloc(sizeof(*mp),GFP_KERNEL);
if (!mp) goto done;
- pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp);
+ pvr2_trace(PVR2_TRACE_CTXT,"pvr2_context %p (create)",mp);
mp->setup_func = setup_func;
mutex_init(&mp->mutex);
+ mutex_lock(&pvr2_context_mutex);
+ mp->exist_prev = pvr2_context_exist_last;
+ mp->exist_next = NULL;
+ pvr2_context_exist_last = mp;
+ if (mp->exist_prev) {
+ mp->exist_prev->exist_next = mp;
+ } else {
+ pvr2_context_exist_first = mp;
+ }
+ mutex_unlock(&pvr2_context_mutex);
mp->hdw = pvr2_hdw_create(intf,devid);
if (!mp->hdw) {
pvr2_context_destroy(mp);
mp = NULL;
goto done;
}
- pvr2_hdw_set_state_callback(mp->hdw,
- (void (*)(void *))pvr2_context_state_check,
- mp);
- pvr2_context_state_check(mp);
+ pvr2_context_set_notify(mp, !0);
done:
return mp;
}
-void pvr2_context_enter(struct pvr2_context *mp)
+static void pvr2_context_reset_input_limits(struct pvr2_context *mp)
+{
+ unsigned int tmsk,mmsk;
+ struct pvr2_channel *cp;
+ struct pvr2_hdw *hdw = mp->hdw;
+ mmsk = pvr2_hdw_get_input_available(hdw);
+ tmsk = mmsk;
+ for (cp = mp->mc_first; cp; cp = cp->mc_next) {
+ if (!cp->input_mask) continue;
+ tmsk &= cp->input_mask;
+ }
+ pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk);
+ pvr2_hdw_commit_ctl(hdw);
+}
+
+
+static void pvr2_context_enter(struct pvr2_context *mp)
{
mutex_lock(&mp->mutex);
- pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp);
}
-void pvr2_context_exit(struct pvr2_context *mp)
+static void pvr2_context_exit(struct pvr2_context *mp)
{
int destroy_flag = 0;
if (!(mp->mc_first || !mp->disconnect_flag)) {
destroy_flag = !0;
}
- pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp);
mutex_unlock(&mp->mutex);
- if (destroy_flag) pvr2_context_destroy(mp);
-}
-
-
-static void pvr2_context_run_checks(struct pvr2_context *mp)
-{
- struct pvr2_channel *ch1,*ch2;
- for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
- ch2 = ch1->mc_next;
- if (ch1->check_func) {
- ch1->check_func(ch1);
- }
- }
+ if (destroy_flag) pvr2_context_notify(mp);
}
void pvr2_context_disconnect(struct pvr2_context *mp)
{
- pvr2_context_enter(mp); do {
- pvr2_hdw_disconnect(mp->hdw);
- mp->disconnect_flag = !0;
- pvr2_context_run_checks(mp);
- } while (0); pvr2_context_exit(mp);
+ pvr2_hdw_disconnect(mp->hdw);
+ mp->disconnect_flag = !0;
+ pvr2_context_notify(mp);
}
void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
{
+ pvr2_context_enter(mp);
cp->hdw = mp->hdw;
cp->mc_head = mp;
cp->mc_next = NULL;
@@ -137,6 +299,7 @@ void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
mp->mc_first = cp;
}
mp->mc_last = cp;
+ pvr2_context_exit(mp);
}
@@ -152,7 +315,10 @@ static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
void pvr2_channel_done(struct pvr2_channel *cp)
{
struct pvr2_context *mp = cp->mc_head;
+ pvr2_context_enter(mp);
+ cp->input_mask = 0;
pvr2_channel_disclaim_stream(cp);
+ pvr2_context_reset_input_limits(mp);
if (cp->mc_next) {
cp->mc_next->mc_prev = cp->mc_prev;
} else {
@@ -164,6 +330,58 @@ void pvr2_channel_done(struct pvr2_channel *cp)
mp->mc_first = cp->mc_next;
}
cp->hdw = NULL;
+ pvr2_context_exit(mp);
+}
+
+
+int pvr2_channel_limit_inputs(struct pvr2_channel *cp,unsigned int cmsk)
+{
+ unsigned int tmsk,mmsk;
+ int ret = 0;
+ struct pvr2_channel *p2;
+ struct pvr2_hdw *hdw = cp->hdw;
+
+ mmsk = pvr2_hdw_get_input_available(hdw);
+ cmsk &= mmsk;
+ if (cmsk == cp->input_mask) {
+ /* No change; nothing to do */
+ return 0;
+ }
+
+ pvr2_context_enter(cp->mc_head);
+ do {
+ if (!cmsk) {
+ cp->input_mask = 0;
+ pvr2_context_reset_input_limits(cp->mc_head);
+ break;
+ }
+ tmsk = mmsk;
+ for (p2 = cp->mc_head->mc_first; p2; p2 = p2->mc_next) {
+ if (p2 == cp) continue;
+ if (!p2->input_mask) continue;
+ tmsk &= p2->input_mask;
+ }
+ if (!(tmsk & cmsk)) {
+ ret = -EPERM;
+ break;
+ }
+ tmsk &= cmsk;
+ if ((ret = pvr2_hdw_set_input_allowed(hdw,mmsk,tmsk)) != 0) {
+ /* Internal failure changing allowed list; probably
+ should not happen, but react if it does. */
+ break;
+ }
+ cp->input_mask = cmsk;
+ pvr2_hdw_commit_ctl(hdw);
+ } while (0);
+ pvr2_context_exit(cp->mc_head);
+ return ret;
+}
+
+
+unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *cp)
+{
+ return cp->input_mask;
}
@@ -173,7 +391,7 @@ int pvr2_channel_claim_stream(struct pvr2_channel *cp,
int code = 0;
pvr2_context_enter(cp->mc_head); do {
if (sp == cp->stream) break;
- if (sp->user) {
+ if (sp && sp->user) {
code = -EBUSY;
break;
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h
index a04187a9322..745e270233c 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-context.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.h
@@ -30,7 +30,6 @@ struct pvr2_stream; /* stream interface - defined elsewhere */
struct pvr2_context; /* All central state */
struct pvr2_channel; /* One I/O pathway to a user */
struct pvr2_context_stream; /* Wrapper for a stream */
-struct pvr2_crit_reg; /* Critical region pointer */
struct pvr2_ioread; /* Low level stream structure */
struct pvr2_context_stream {
@@ -41,11 +40,16 @@ struct pvr2_context_stream {
struct pvr2_context {
struct pvr2_channel *mc_first;
struct pvr2_channel *mc_last;
+ struct pvr2_context *exist_next;
+ struct pvr2_context *exist_prev;
+ struct pvr2_context *notify_next;
+ struct pvr2_context *notify_prev;
struct pvr2_hdw *hdw;
struct pvr2_context_stream video_stream;
struct mutex mutex;
+ int notify_flag;
+ int initialized_flag;
int disconnect_flag;
- int init_flag;
/* Called after pvr2_context initialization is complete */
void (*setup_func)(struct pvr2_context *);
@@ -58,12 +62,10 @@ struct pvr2_channel {
struct pvr2_channel *mc_prev;
struct pvr2_context_stream *stream;
struct pvr2_hdw *hdw;
+ unsigned int input_mask;
void (*check_func)(struct pvr2_channel *);
};
-void pvr2_context_enter(struct pvr2_context *);
-void pvr2_context_exit(struct pvr2_context *);
-
struct pvr2_context *pvr2_context_create(struct usb_interface *intf,
const struct usb_device_id *devid,
void (*setup_func)(struct pvr2_context *));
@@ -71,11 +73,15 @@ void pvr2_context_disconnect(struct pvr2_context *);
void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *);
void pvr2_channel_done(struct pvr2_channel *);
+int pvr2_channel_limit_inputs(struct pvr2_channel *,unsigned int);
+unsigned int pvr2_channel_get_limited_inputs(struct pvr2_channel *);
int pvr2_channel_claim_stream(struct pvr2_channel *,
struct pvr2_context_stream *);
struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
struct pvr2_context_stream *);
+int pvr2_context_global_init(void);
+void pvr2_context_global_done(void);
#endif /* __PVRUSB2_CONTEXT_H */
/*
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
index 5a3e8d21a38..91a42f2473a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
@@ -30,6 +30,9 @@ static int pvr2_ctrl_range_check(struct pvr2_ctrl *cptr,int val)
{
if (cptr->info->check_value) {
if (!cptr->info->check_value(cptr,val)) return -ERANGE;
+ } else if (cptr->info->type == pvr2_ctl_enum) {
+ if (val < 0) return -ERANGE;
+ if (val >= cptr->info->def.type_enum.count) return -ERANGE;
} else {
int lim;
lim = cptr->info->def.type_int.min_value;
@@ -63,13 +66,10 @@ int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
if (cptr->info->set_value) {
if (cptr->info->type == pvr2_ctl_bitmask) {
mask &= cptr->info->def.type_bitmask.valid_bits;
- } else if (cptr->info->type == pvr2_ctl_int) {
+ } else if ((cptr->info->type == pvr2_ctl_int)||
+ (cptr->info->type == pvr2_ctl_enum)) {
ret = pvr2_ctrl_range_check(cptr,val);
if (ret < 0) break;
- } else if (cptr->info->type == pvr2_ctl_enum) {
- if (val >= cptr->info->def.type_enum.count) {
- break;
- }
} else if (cptr->info->type != pvr2_ctl_bool) {
break;
}
@@ -204,8 +204,7 @@ int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val,
if (cptr->info->type == pvr2_ctl_enum) {
const char **names;
names = cptr->info->def.type_enum.value_names;
- if ((val >= 0) &&
- (val < cptr->info->def.type_enum.count)) {
+ if (pvr2_ctrl_range_check(cptr,val) == 0) {
if (names[val]) {
*blen = scnprintf(
bptr,bmax,"%s",
@@ -528,10 +527,8 @@ int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr,
ptr,len,valptr,
cptr->info->def.type_enum.value_names,
cptr->info->def.type_enum.count);
- if ((ret >= 0) &&
- ((*valptr < 0) ||
- (*valptr >= cptr->info->def.type_enum.count))) {
- ret = -ERANGE;
+ if (ret >= 0) {
+ ret = pvr2_ctrl_range_check(cptr,*valptr);
}
if (maskptr) *maskptr = ~0;
} else if (cptr->info->type == pvr2_ctl_bitmask) {
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index ffdc45c324e..97350b048b8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -84,7 +84,9 @@ static const struct routing_scheme_item routing_schemegv[] = {
.vid = CX25840_COMPOSITE2,
.aud = CX25840_AUDIO5,
},
- [PVR2_CVAL_INPUT_RADIO] = { /* Treat the same as composite */
+ [PVR2_CVAL_INPUT_RADIO] = {
+ /* line-in is used for radio and composite. A GPIO is
+ used to switch between the two choices. */
.vid = CX25840_COMPOSITE1,
.aud = CX25840_AUDIO_SERIAL,
},
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
index fca49d8a931..11537ddf8aa 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debug.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -39,7 +39,7 @@ extern int pvrusb2_debug;
#define PVR2_TRACE_EEPROM (1 << 10) /* eeprom parsing / report */
#define PVR2_TRACE_STRUCT (1 << 11) /* internal struct creation */
#define PVR2_TRACE_OPEN_CLOSE (1 << 12) /* application open / close */
-#define PVR2_TRACE_CREG (1 << 13) /* Main critical region entry / exit */
+#define PVR2_TRACE_CTXT (1 << 13) /* Main context tracking */
#define PVR2_TRACE_SYSFS (1 << 14) /* Sysfs driven I/O */
#define PVR2_TRACE_FIRMWARE (1 << 15) /* firmware upload actions */
#define PVR2_TRACE_CHIPS (1 << 16) /* chip broadcast operation */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
index b0687430fdd..b53121c78ff 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -164,6 +164,8 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
int ccnt;
int ret;
u32 gpio_dir,gpio_in,gpio_out;
+ struct pvr2_stream_stats stats;
+ struct pvr2_stream *sp;
ret = pvr2_hdw_is_hsm(hdw);
ccnt = scnprintf(buf,acnt,"USB link speed: %s\n",
@@ -182,6 +184,24 @@ int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
pvr2_hdw_get_streaming(hdw) ? "on" : "off");
bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ sp = pvr2_hdw_get_video_stream(hdw);
+ if (sp) {
+ pvr2_stream_get_stats(sp, &stats, 0);
+ ccnt = scnprintf(
+ buf,acnt,
+ "Bytes streamed=%u"
+ " URBs: queued=%u idle=%u ready=%u"
+ " processed=%u failed=%u\n",
+ stats.bytes_processed,
+ stats.buffers_in_queue,
+ stats.buffers_in_idle,
+ stats.buffers_in_ready,
+ stats.buffers_processed,
+ stats.buffers_failed);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
+
return bcnt;
}
@@ -220,6 +240,10 @@ static int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
return pvr2_hdw_cmd_decoder_reset(hdw);
} else if (debugifc_match_keyword(wptr,wlen,"worker")) {
return pvr2_hdw_untrip(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"usbstats")) {
+ pvr2_stream_get_stats(pvr2_hdw_get_video_stream(hdw),
+ NULL, !0);
+ return 0;
}
return -EINVAL;
} else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
index fe9991c10cf..2dd06a90adc 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
@@ -32,7 +32,15 @@ pvr2_device_desc structures.
/* This is needed in order to pull in tuner type ids... */
#include <linux/i2c.h>
#include <media/tuner.h>
-
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+#include "pvrusb2-hdw-internal.h"
+#include "lgdt330x.h"
+#include "s5h1409.h"
+#include "tda10048.h"
+#include "tda18271.h"
+#include "tda8290.h"
+#include "tuner-simple.h"
+#endif
/*------------------------------------------------------------------------*/
@@ -49,14 +57,19 @@ static const char *pvr2_fw1_names_29xxx[] = {
};
static const struct pvr2_device_desc pvr2_device_29xxx = {
- .description = "WinTV PVR USB2 Model Category 29xxxx",
+ .description = "WinTV PVR USB2 Model Category 29xxx",
.shortname = "29xxx",
.client_modules.lst = pvr2_client_29xxx,
.client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx),
.fx2_firmware.lst = pvr2_fw1_names_29xxx,
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
.flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
};
@@ -75,7 +88,7 @@ static const char *pvr2_fw1_names_24xxx[] = {
};
static const struct pvr2_device_desc pvr2_device_24xxx = {
- .description = "WinTV PVR USB2 Model Category 24xxxx",
+ .description = "WinTV PVR USB2 Model Category 24xxx",
.shortname = "24xxx",
.client_modules.lst = pvr2_client_24xxx,
.client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx),
@@ -85,7 +98,12 @@ static const struct pvr2_device_desc pvr2_device_24xxx = {
.flag_has_wm8775 = !0,
.flag_has_hauppauge_rom = !0,
.flag_has_hauppauge_custom_ir = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
};
@@ -105,6 +123,30 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = {
.client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2),
.flag_has_cx25840 = !0,
.default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+ .flag_has_fmradio = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
+};
+
+
+
+/*------------------------------------------------------------------------*/
+/* GOTVIEW USB2.0 DVD Deluxe */
+
+/* (same module list as gotview_2) */
+
+static const struct pvr2_device_desc pvr2_device_gotview_2d = {
+ .description = "Gotview USB 2.0 DVD Deluxe",
+ .shortname = "gv2d",
+ .client_modules.lst = pvr2_client_gotview_2,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_GOTVIEW,
};
@@ -114,6 +156,38 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = {
/*------------------------------------------------------------------------*/
/* OnAir Creator */
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct lgdt330x_config pvr2_lgdt3303_config = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .clock_polarity_flip = 1,
+};
+
+static int pvr2_lgdt3303_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3303_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(simple_tuner_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x61,
+ TUNER_LG_TDVS_H06XF);
+
+ return 0;
+}
+
+struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
+ .frontend_attach = pvr2_lgdt3303_attach,
+ .tuner_attach = pvr2_lgh06xf_attach,
+};
+#endif
+
static const char *pvr2_client_onair_creator[] = {
"saa7115",
"tuner",
@@ -126,7 +200,16 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = {
.client_modules.lst = pvr2_client_onair_creator,
.client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator),
.default_tuner_type = TUNER_LG_TDVS_H06XF,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_onair_creator_fe_props,
+#endif
};
#endif
@@ -136,6 +219,37 @@ static const struct pvr2_device_desc pvr2_device_onair_creator = {
/*------------------------------------------------------------------------*/
/* OnAir USB 2.0 */
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct lgdt330x_config pvr2_lgdt3302_config = {
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3302,
+};
+
+static int pvr2_lgdt3302_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(lgdt330x_attach, &pvr2_lgdt3302_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(simple_tuner_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x61,
+ TUNER_PHILIPS_FCV1236D);
+
+ return 0;
+}
+
+struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
+ .frontend_attach = pvr2_lgdt3302_attach,
+ .tuner_attach = pvr2_fcv1236d_attach,
+};
+#endif
+
static const char *pvr2_client_onair_usb2[] = {
"saa7115",
"tuner",
@@ -147,8 +261,17 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
.shortname = "oa2",
.client_modules.lst = pvr2_client_onair_usb2,
.client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2),
- .default_tuner_type = TUNER_PHILIPS_ATSC,
+ .default_tuner_type = TUNER_PHILIPS_FCV1236D,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_onair_usb2_fe_props,
+#endif
};
#endif
@@ -157,6 +280,50 @@ static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
/*------------------------------------------------------------------------*/
/* Hauppauge PVR-USB2 Model 73xxx */
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct tda10048_config hauppauge_tda10048_config = {
+ .demod_address = 0x10 >> 1,
+ .output_mode = TDA10048_PARALLEL_OUTPUT,
+ .fwbulkwritelen = TDA10048_BULKWRITE_50,
+ .inversion = TDA10048_INVERSION_ON,
+};
+
+static struct tda829x_config tda829x_no_probe = {
+ .probe_tuner = TDA829X_DONT_PROBE,
+};
+
+static struct tda18271_config hauppauge_tda18271_dvb_config = {
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static int pvr2_tda10048_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(tda10048_attach, &hauppauge_tda10048_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(tda829x_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, adap->fe, 0x60,
+ &adap->channel.hdw->i2c_adap,
+ &hauppauge_tda18271_dvb_config);
+
+ return 0;
+}
+
+struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
+ .frontend_attach = pvr2_tda10048_attach,
+ .tuner_attach = pvr2_73xxx_tda18271_8295_attach,
+};
+#endif
+
static const char *pvr2_client_73xxx[] = {
"cx25840",
"tuner",
@@ -167,7 +334,7 @@ static const char *pvr2_fw1_names_73xxx[] = {
};
static const struct pvr2_device_desc pvr2_device_73xxx = {
- .description = "WinTV PVR USB2 Model Category 73xxxx",
+ .description = "WinTV PVR USB2 Model Category 73xxx",
.shortname = "73xxx",
.client_modules.lst = pvr2_client_73xxx,
.client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx),
@@ -175,15 +342,14 @@ static const struct pvr2_device_desc pvr2_device_73xxx = {
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx),
.flag_has_cx25840 = !0,
.flag_has_hauppauge_rom = !0,
-#if 0
.flag_has_analogtuner = !0,
.flag_has_composite = !0,
.flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
.digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
-#else
- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_73xxx_dvb_props,
#endif
};
@@ -192,6 +358,56 @@ static const struct pvr2_device_desc pvr2_device_73xxx = {
/*------------------------------------------------------------------------*/
/* Hauppauge PVR-USB2 Model 75xxx */
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+static struct s5h1409_config pvr2_s5h1409_config = {
+ .demod_address = 0x32 >> 1,
+ .output_mode = S5H1409_PARALLEL_OUTPUT,
+ .gpio = S5H1409_GPIO_OFF,
+ .qam_if = 4000,
+ .inversion = S5H1409_INVERSION_ON,
+ .status_mode = S5H1409_DEMODLOCKING,
+};
+
+static struct tda18271_std_map hauppauge_tda18271_std_map = {
+ .atsc_6 = { .if_freq = 5380, .agc_mode = 3, .std = 3,
+ .if_lvl = 6, .rfagc_top = 0x37, },
+ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 0,
+ .if_lvl = 6, .rfagc_top = 0x37, },
+};
+
+static struct tda18271_config hauppauge_tda18271_config = {
+ .std_map = &hauppauge_tda18271_std_map,
+ .gate = TDA18271_GATE_ANALOG,
+};
+
+static int pvr2_s5h1409_attach(struct pvr2_dvb_adapter *adap)
+{
+ adap->fe = dvb_attach(s5h1409_attach, &pvr2_s5h1409_config,
+ &adap->channel.hdw->i2c_adap);
+ if (adap->fe)
+ return 0;
+
+ return -EIO;
+}
+
+static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+{
+ dvb_attach(tda829x_attach, adap->fe,
+ &adap->channel.hdw->i2c_adap, 0x42,
+ &tda829x_no_probe);
+ dvb_attach(tda18271_attach, adap->fe, 0x60,
+ &adap->channel.hdw->i2c_adap,
+ &hauppauge_tda18271_config);
+
+ return 0;
+}
+
+struct pvr2_dvb_props pvr2_750xx_dvb_props = {
+ .frontend_attach = pvr2_s5h1409_attach,
+ .tuner_attach = pvr2_tda18271_8295_attach,
+};
+#endif
+
static const char *pvr2_client_75xxx[] = {
"cx25840",
"tuner",
@@ -201,17 +417,43 @@ static const char *pvr2_fw1_names_75xxx[] = {
"v4l-pvrusb2-73xxx-01.fw",
};
-static const struct pvr2_device_desc pvr2_device_75xxx = {
- .description = "WinTV PVR USB2 Model Category 75xxxx",
- .shortname = "75xxx",
+static const struct pvr2_device_desc pvr2_device_750xx = {
+ .description = "WinTV PVR USB2 Model Category 750xx",
+ .shortname = "750xx",
+ .client_modules.lst = pvr2_client_75xxx,
+ .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+ .flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
+ .default_std_mask = V4L2_STD_NTSC_M,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ .dvb_props = &pvr2_750xx_dvb_props,
+#endif
+};
+
+static const struct pvr2_device_desc pvr2_device_751xx = {
+ .description = "WinTV PVR USB2 Model Category 751xx",
+ .shortname = "751xx",
.client_modules.lst = pvr2_client_75xxx,
.client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx),
.fx2_firmware.lst = pvr2_fw1_names_75xxx,
.fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
.flag_has_cx25840 = !0,
.flag_has_hauppauge_rom = !0,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
.signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_HAUPPAUGE,
.default_std_mask = V4L2_STD_NTSC_M,
+ .led_scheme = PVR2_LED_SCHEME_HAUPPAUGE,
};
@@ -225,6 +467,8 @@ struct usb_device_id pvr2_device_table[] = {
.driver_info = (kernel_ulong_t)&pvr2_device_24xxx},
{ USB_DEVICE(0x1164, 0x0622),
.driver_info = (kernel_ulong_t)&pvr2_device_gotview_2},
+ { USB_DEVICE(0x1164, 0x0602),
+ .driver_info = (kernel_ulong_t)&pvr2_device_gotview_2d},
#ifdef CONFIG_VIDEO_PVRUSB2_ONAIR_CREATOR
{ USB_DEVICE(0x11ba, 0x1003),
.driver_info = (kernel_ulong_t)&pvr2_device_onair_creator},
@@ -236,9 +480,9 @@ struct usb_device_id pvr2_device_table[] = {
{ USB_DEVICE(0x2040, 0x7300),
.driver_info = (kernel_ulong_t)&pvr2_device_73xxx},
{ USB_DEVICE(0x2040, 0x7500),
- .driver_info = (kernel_ulong_t)&pvr2_device_75xxx},
+ .driver_info = (kernel_ulong_t)&pvr2_device_750xx},
{ USB_DEVICE(0x2040, 0x7501),
- .driver_info = (kernel_ulong_t)&pvr2_device_75xxx},
+ .driver_info = (kernel_ulong_t)&pvr2_device_751xx},
{ }
};
diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
index 64b467f0637..c2e2b06fe2e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
@@ -23,6 +23,9 @@
#include <linux/mod_devicetable.h>
#include <linux/videodev2.h>
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+#include "pvrusb2-dvb.h"
+#endif
/*
@@ -39,6 +42,13 @@ struct pvr2_string_table {
#define PVR2_ROUTING_SCHEME_HAUPPAUGE 0
#define PVR2_ROUTING_SCHEME_GOTVIEW 1
+#define PVR2_DIGITAL_SCHEME_NONE 0
+#define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1
+#define PVR2_DIGITAL_SCHEME_ONAIR 2
+
+#define PVR2_LED_SCHEME_NONE 0
+#define PVR2_LED_SCHEME_HAUPPAUGE 1
+
/* This describes a particular hardware type (except for the USB device ID
which must live in a separate structure due to environmental
constraints). See the top of pvrusb2-hdw.c for where this is
@@ -58,40 +68,64 @@ struct pvr2_device_desc {
was initialized from internal ROM. */
struct pvr2_string_table fx2_firmware;
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ /* callback functions to handle attachment of digital tuner & demod */
+ struct pvr2_dvb_props *dvb_props;
+
+#endif
+ /* Initial standard bits to use for this device, if not zero.
+ Anything set here is also implied as an available standard.
+ Note: This is ignored if overridden on the module load line via
+ the video_std module option. */
+ v4l2_std_id default_std_mask;
+
+ /* V4L tuner type ID to use with this device (only used if the
+ driver could not discover the type any other way). */
+ int default_tuner_type;
+
/* Signal routing scheme used by device, contains one of
PVR2_ROUTING_SCHEME_XXX. Schemes have to be defined as we
encounter them. This is an arbitrary integer scheme id; its
meaning is contained entirely within the driver and is
interpreted by logic which must send commands to the chip-level
drivers (search for things which touch this field). */
- unsigned int signal_routing_scheme;
+ unsigned char signal_routing_scheme;
- /* V4L tuner type ID to use with this device (only used if the
- driver could not discover the type any other way). */
- int default_tuner_type;
+ /* Indicates scheme for controlling device's LED (if any). The
+ driver will turn on the LED when streaming is underway. This
+ contains one of PVR2_LED_SCHEME_XXX. */
+ unsigned char led_scheme;
- /* Initial standard bits to use for this device, if not zero.
- Anything set here is also implied as an available standard.
- Note: This is ignored if overridden on the module load line via
- the video_std module option. */
- v4l2_std_id default_std_mask;
+ /* Control scheme to use if there is a digital tuner. This
+ contains one of PVR2_DIGITAL_SCHEME_XXX. This is an arbitrary
+ integer scheme id; its meaning is contained entirely within the
+ driver and is interpreted by logic which must control the
+ streaming pathway (search for things which touch this field). */
+ unsigned char digital_control_scheme;
/* If set, we don't bother trying to load cx23416 firmware. */
- char flag_skip_cx23416_firmware;
+ int flag_skip_cx23416_firmware:1;
+
+ /* If set, the encoder must be healthy in order for digital mode to
+ work (otherwise we assume that digital streaming will work even
+ if we fail to locate firmware for the encoder). If the device
+ doesn't support digital streaming then this flag has no
+ effect. */
+ int flag_digital_requires_cx23416:1;
/* Device has a hauppauge eeprom which we can interrogate. */
- char flag_has_hauppauge_rom;
+ int flag_has_hauppauge_rom:1;
/* Device does not require a powerup command to be issued. */
- char flag_no_powerup;
+ int flag_no_powerup:1;
/* Device has a cx25840 - this enables special additional logic to
handle it. */
- char flag_has_cx25840;
+ int flag_has_cx25840:1;
/* Device has a wm8775 - this enables special additional logic to
ensure that it is found. */
- char flag_has_wm8775;
+ int flag_has_wm8775:1;
/* Device has IR hardware that can be faked into looking like a
normal Hauppauge i2c IR receiver. This is currently very
@@ -101,7 +135,15 @@ struct pvr2_device_desc {
to virtualize the presence of the non-existant IR receiver chip and
implement the virtual receiver in terms of appropriate FX2
commands. */
- char flag_has_hauppauge_custom_ir;
+ int flag_has_hauppauge_custom_ir:1;
+
+ /* These bits define which kinds of sources the device can handle.
+ Note: Digital tuner presence is inferred by the
+ digital_control_scheme enumeration. */
+ int flag_has_fmradio:1; /* Has FM radio receiver */
+ int flag_has_analogtuner:1; /* Has analog tuner */
+ int flag_has_composite:1; /* Has composite input */
+ int flag_has_svideo:1; /* Has s-video input */
};
extern struct usb_device_id pvr2_device_table[];
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
new file mode 100644
index 00000000000..2e64f98d124
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
@@ -0,0 +1,425 @@
+/*
+ * pvrusb2-dvb.c - linux-dvb api interface to the pvrusb2 driver.
+ *
+ * Copyright (C) 2007, 2008 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/kthread.h>
+#include <linux/freezer.h>
+#include "dvbdev.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-io.h"
+#include "pvrusb2-dvb.h"
+
+DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+static int pvr2_dvb_feed_func(struct pvr2_dvb_adapter *adap)
+{
+ int ret;
+ unsigned int count;
+ struct pvr2_buffer *bp;
+ struct pvr2_stream *stream;
+
+ printk(KERN_DEBUG "dvb thread started\n");
+ set_freezable();
+
+ stream = adap->channel.stream->stream;
+
+ for (;;) {
+ if (kthread_should_stop()) break;
+
+ /* Not sure about this... */
+ try_to_freeze();
+
+ bp = pvr2_stream_get_ready_buffer(stream);
+ if (bp != NULL) {
+ count = pvr2_buffer_get_count(bp);
+ if (count) {
+ dvb_dmx_swfilter(
+ &adap->demux,
+ adap->buffer_storage[
+ pvr2_buffer_get_id(bp)],
+ count);
+ } else {
+ ret = pvr2_buffer_get_status(bp);
+ if (ret < 0) break;
+ }
+ ret = pvr2_buffer_queue(bp);
+ if (ret < 0) break;
+
+ /* Since we know we did something to a buffer,
+ just go back and try again. No point in
+ blocking unless we really ran out of
+ buffers to process. */
+ continue;
+ }
+
+
+ /* Wait until more buffers become available or we're
+ told not to wait any longer. */
+ ret = wait_event_interruptible(
+ adap->buffer_wait_data,
+ (pvr2_stream_get_ready_count(stream) > 0) ||
+ kthread_should_stop());
+ if (ret < 0) break;
+ }
+
+ /* If we get here and ret is < 0, then an error has occurred.
+ Probably would be a good idea to communicate that to DVB core... */
+
+ printk(KERN_DEBUG "dvb thread stopped\n");
+
+ return 0;
+}
+
+static int pvr2_dvb_feed_thread(void *data)
+{
+ int stat = pvr2_dvb_feed_func(data);
+ /* from videobuf-dvb.c: */
+ while (!kthread_should_stop()) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule();
+ }
+ return stat;
+}
+
+static void pvr2_dvb_notify(struct pvr2_dvb_adapter *adap)
+{
+ wake_up(&adap->buffer_wait_data);
+}
+
+static void pvr2_dvb_stream_end(struct pvr2_dvb_adapter *adap)
+{
+ unsigned int idx;
+ struct pvr2_stream *stream;
+
+ if (adap->thread) {
+ kthread_stop(adap->thread);
+ adap->thread = NULL;
+ }
+
+ if (adap->channel.stream) {
+ stream = adap->channel.stream->stream;
+ } else {
+ stream = NULL;
+ }
+ if (stream) {
+ pvr2_hdw_set_streaming(adap->channel.hdw, 0);
+ pvr2_stream_set_callback(stream, NULL, NULL);
+ pvr2_stream_kill(stream);
+ pvr2_stream_set_buffer_count(stream, 0);
+ pvr2_channel_claim_stream(&adap->channel, NULL);
+ }
+
+ if (adap->stream_run) {
+ for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
+ if (!(adap->buffer_storage[idx])) continue;
+ kfree(adap->buffer_storage[idx]);
+ adap->buffer_storage[idx] = 0;
+ }
+ adap->stream_run = 0;
+ }
+}
+
+static int pvr2_dvb_stream_do_start(struct pvr2_dvb_adapter *adap)
+{
+ struct pvr2_context *pvr = adap->channel.mc_head;
+ unsigned int idx;
+ int ret;
+ struct pvr2_buffer *bp;
+ struct pvr2_stream *stream = 0;
+
+ if (adap->stream_run) return -EIO;
+
+ ret = pvr2_channel_claim_stream(&adap->channel, &pvr->video_stream);
+ /* somebody else already has the stream */
+ if (ret < 0) return ret;
+
+ stream = adap->channel.stream->stream;
+
+ for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
+ adap->buffer_storage[idx] = kmalloc(PVR2_DVB_BUFFER_SIZE,
+ GFP_KERNEL);
+ if (!(adap->buffer_storage[idx])) return -ENOMEM;
+ }
+
+ pvr2_stream_set_callback(pvr->video_stream.stream,
+ (pvr2_stream_callback) pvr2_dvb_notify, adap);
+
+ ret = pvr2_stream_set_buffer_count(stream, PVR2_DVB_BUFFER_COUNT);
+ if (ret < 0) return ret;
+
+ for (idx = 0; idx < PVR2_DVB_BUFFER_COUNT; idx++) {
+ bp = pvr2_stream_get_buffer(stream, idx);
+ pvr2_buffer_set_buffer(bp,
+ adap->buffer_storage[idx],
+ PVR2_DVB_BUFFER_SIZE);
+ }
+
+ ret = pvr2_hdw_set_streaming(adap->channel.hdw, 1);
+ if (ret < 0) return ret;
+
+ while ((bp = pvr2_stream_get_idle_buffer(stream)) != 0) {
+ ret = pvr2_buffer_queue(bp);
+ if (ret < 0) return ret;
+ }
+
+ adap->thread = kthread_run(pvr2_dvb_feed_thread, adap, "pvrusb2-dvb");
+
+ if (IS_ERR(adap->thread)) {
+ ret = PTR_ERR(adap->thread);
+ adap->thread = NULL;
+ return ret;
+ }
+
+ adap->stream_run = !0;
+
+ return 0;
+}
+
+static int pvr2_dvb_stream_start(struct pvr2_dvb_adapter *adap)
+{
+ int ret = pvr2_dvb_stream_do_start(adap);
+ if (ret < 0) pvr2_dvb_stream_end(adap);
+ return ret;
+}
+
+static int pvr2_dvb_ctrl_feed(struct dvb_demux_feed *dvbdmxfeed, int onoff)
+{
+ struct pvr2_dvb_adapter *adap = dvbdmxfeed->demux->priv;
+ int ret = 0;
+
+ if (adap == NULL) return -ENODEV;
+
+ mutex_lock(&adap->lock);
+ do {
+ if (onoff) {
+ if (!adap->feedcount) {
+ printk(KERN_DEBUG "start feeding\n");
+ ret = pvr2_dvb_stream_start(adap);
+ if (ret < 0) break;
+ }
+ (adap->feedcount)++;
+ } else if (adap->feedcount > 0) {
+ (adap->feedcount)--;
+ if (!adap->feedcount) {
+ printk(KERN_DEBUG "stop feeding\n");
+ pvr2_dvb_stream_end(adap);
+ }
+ }
+ } while (0);
+ mutex_unlock(&adap->lock);
+
+ return ret;
+}
+
+static int pvr2_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ printk(KERN_DEBUG "start pid: 0x%04x, feedtype: %d\n",
+ dvbdmxfeed->pid, dvbdmxfeed->type);
+ return pvr2_dvb_ctrl_feed(dvbdmxfeed, 1);
+}
+
+static int pvr2_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+{
+ printk(KERN_DEBUG "stop pid: 0x%04x, feedtype: %d\n",
+ dvbdmxfeed->pid, dvbdmxfeed->type);
+ return pvr2_dvb_ctrl_feed(dvbdmxfeed, 0);
+}
+
+static int pvr2_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
+{
+ struct pvr2_dvb_adapter *adap = fe->dvb->priv;
+ return pvr2_channel_limit_inputs(
+ &adap->channel,
+ (acquire ? (1 << PVR2_CVAL_INPUT_DTV) : 0));
+}
+
+static int pvr2_dvb_adapter_init(struct pvr2_dvb_adapter *adap)
+{
+ int ret;
+
+ ret = dvb_register_adapter(&adap->dvb_adap, "pvrusb2-dvb",
+ THIS_MODULE/*&hdw->usb_dev->owner*/,
+ &adap->channel.hdw->usb_dev->dev,
+ adapter_nr);
+ if (ret < 0) {
+ err("dvb_register_adapter failed: error %d", ret);
+ goto err;
+ }
+ adap->dvb_adap.priv = adap;
+
+ adap->demux.dmx.capabilities = DMX_TS_FILTERING |
+ DMX_SECTION_FILTERING |
+ DMX_MEMORY_BASED_FILTERING;
+ adap->demux.priv = adap;
+ adap->demux.filternum = 256;
+ adap->demux.feednum = 256;
+ adap->demux.start_feed = pvr2_dvb_start_feed;
+ adap->demux.stop_feed = pvr2_dvb_stop_feed;
+ adap->demux.write_to_decoder = NULL;
+
+ ret = dvb_dmx_init(&adap->demux);
+ if (ret < 0) {
+ err("dvb_dmx_init failed: error %d", ret);
+ goto err_dmx;
+ }
+
+ adap->dmxdev.filternum = adap->demux.filternum;
+ adap->dmxdev.demux = &adap->demux.dmx;
+ adap->dmxdev.capabilities = 0;
+
+ ret = dvb_dmxdev_init(&adap->dmxdev, &adap->dvb_adap);
+ if (ret < 0) {
+ err("dvb_dmxdev_init failed: error %d", ret);
+ goto err_dmx_dev;
+ }
+
+ dvb_net_init(&adap->dvb_adap, &adap->dvb_net, &adap->demux.dmx);
+
+ return 0;
+
+err_dmx_dev:
+ dvb_dmx_release(&adap->demux);
+err_dmx:
+ dvb_unregister_adapter(&adap->dvb_adap);
+err:
+ return ret;
+}
+
+static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap)
+{
+ printk(KERN_DEBUG "unregistering DVB devices\n");
+ dvb_net_release(&adap->dvb_net);
+ adap->demux.dmx.close(&adap->demux.dmx);
+ dvb_dmxdev_release(&adap->dmxdev);
+ dvb_dmx_release(&adap->demux);
+ dvb_unregister_adapter(&adap->dvb_adap);
+ return 0;
+}
+
+static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
+{
+ struct pvr2_hdw *hdw = adap->channel.hdw;
+ struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props;
+ int ret = 0;
+
+ if (dvb_props == NULL) {
+ err("fe_props not defined!");
+ return -EINVAL;
+ }
+
+ ret = pvr2_channel_limit_inputs(
+ &adap->channel,
+ (1 << PVR2_CVAL_INPUT_DTV));
+ if (ret) {
+ err("failed to grab control of dtv input (code=%d)",
+ ret);
+ return ret;
+ }
+
+ if (dvb_props->frontend_attach == NULL) {
+ err("frontend_attach not defined!");
+ ret = -EINVAL;
+ goto done;
+ }
+
+ if ((dvb_props->frontend_attach(adap) == 0) && (adap->fe)) {
+
+ if (dvb_register_frontend(&adap->dvb_adap, adap->fe)) {
+ err("frontend registration failed!");
+ dvb_frontend_detach(adap->fe);
+ adap->fe = NULL;
+ ret = -ENODEV;
+ goto done;
+ }
+
+ if (dvb_props->tuner_attach)
+ dvb_props->tuner_attach(adap);
+
+ if (adap->fe->ops.analog_ops.standby)
+ adap->fe->ops.analog_ops.standby(adap->fe);
+
+ /* Ensure all frontends negotiate bus access */
+ adap->fe->ops.ts_bus_ctrl = pvr2_dvb_bus_ctrl;
+
+ } else {
+ err("no frontend was attached!");
+ ret = -ENODEV;
+ return ret;
+ }
+
+ done:
+ pvr2_channel_limit_inputs(&adap->channel, 0);
+ return ret;
+}
+
+static int pvr2_dvb_frontend_exit(struct pvr2_dvb_adapter *adap)
+{
+ if (adap->fe != NULL) {
+ dvb_unregister_frontend(adap->fe);
+ dvb_frontend_detach(adap->fe);
+ }
+ return 0;
+}
+
+static void pvr2_dvb_destroy(struct pvr2_dvb_adapter *adap)
+{
+ pvr2_dvb_stream_end(adap);
+ pvr2_dvb_frontend_exit(adap);
+ pvr2_dvb_adapter_exit(adap);
+ pvr2_channel_done(&adap->channel);
+ kfree(adap);
+}
+
+static void pvr2_dvb_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_dvb_adapter *adap;
+ adap = container_of(chp, struct pvr2_dvb_adapter, channel);
+ if (!adap->channel.mc_head->disconnect_flag) return;
+ pvr2_dvb_destroy(adap);
+}
+
+struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr)
+{
+ int ret = 0;
+ struct pvr2_dvb_adapter *adap;
+ if (!pvr->hdw->hdw_desc->dvb_props) {
+ /* Device lacks a digital interface so don't set up
+ the DVB side of the driver either. For now. */
+ return NULL;
+ }
+ adap = kzalloc(sizeof(*adap), GFP_KERNEL);
+ if (!adap) return adap;
+ pvr2_channel_init(&adap->channel, pvr);
+ adap->channel.check_func = pvr2_dvb_internal_check;
+ init_waitqueue_head(&adap->buffer_wait_data);
+ mutex_init(&adap->lock);
+ ret = pvr2_dvb_adapter_init(adap);
+ if (ret < 0) goto fail1;
+ ret = pvr2_dvb_frontend_init(adap);
+ if (ret < 0) goto fail2;
+ return adap;
+
+fail2:
+ pvr2_dvb_adapter_exit(adap);
+fail1:
+ pvr2_channel_done(&adap->channel);
+ return NULL;
+}
+
diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.h b/drivers/media/video/pvrusb2/pvrusb2-dvb.h
new file mode 100644
index 00000000000..884ff916a35
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.h
@@ -0,0 +1,41 @@
+#ifndef __PVRUSB2_DVB_H__
+#define __PVRUSB2_DVB_H__
+
+#include "dvb_frontend.h"
+#include "dvb_demux.h"
+#include "dvb_net.h"
+#include "dmxdev.h"
+#include "pvrusb2-context.h"
+
+#define PVR2_DVB_BUFFER_COUNT 32
+#define PVR2_DVB_BUFFER_SIZE PAGE_ALIGN(0x4000)
+
+struct pvr2_dvb_adapter {
+ struct pvr2_channel channel;
+
+ struct dvb_adapter dvb_adap;
+ struct dmxdev dmxdev;
+ struct dvb_demux demux;
+ struct dvb_net dvb_net;
+ struct dvb_frontend *fe;
+
+ int feedcount;
+ int max_feed_count;
+
+ struct task_struct *thread;
+ struct mutex lock;
+
+ unsigned int stream_run:1;
+
+ wait_queue_head_t buffer_wait_data;
+ char *buffer_storage[PVR2_DVB_BUFFER_COUNT];
+};
+
+struct pvr2_dvb_props {
+ int (*frontend_attach) (struct pvr2_dvb_adapter *);
+ int (*tuner_attach) (struct pvr2_dvb_adapter *);
+};
+
+struct pvr2_dvb_adapter *pvr2_dvb_create(struct pvr2_context *pvr);
+
+#endif /* __PVRUSB2_DVB_H__ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 64062879981..c46d367f747 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -278,11 +278,20 @@ static int pvr2_encoder_cmd(void *ctxt,
ret = -EBUSY;
}
if (ret) {
+ del_timer_sync(&hdw->encoder_run_timer);
hdw->state_encoder_ok = 0;
pvr2_trace(PVR2_TRACE_STBITS,
"State bit %s <-- %s",
"state_encoder_ok",
(hdw->state_encoder_ok ? "true" : "false"));
+ if (hdw->state_encoder_runok) {
+ hdw->state_encoder_runok = 0;
+ pvr2_trace(PVR2_TRACE_STBITS,
+ "State bit %s <-- %s",
+ "state_encoder_runok",
+ (hdw->state_encoder_runok ?
+ "true" : "false"));
+ }
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
"Giving up on command."
@@ -480,10 +489,6 @@ int pvr2_encoder_start(struct pvr2_hdw *hdw)
/* unmask some interrupts */
pvr2_write_register(hdw, 0x0048, 0xbfffffff);
- /* change some GPIO data */
- pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481);
- pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
-
pvr2_encoder_vcmd(hdw,CX2341X_ENC_MUTE_VIDEO,1,
hdw->input_val == PVR2_CVAL_INPUT_RADIO ? 1 : 0);
@@ -526,12 +531,6 @@ int pvr2_encoder_stop(struct pvr2_hdw *hdw)
break;
}
- /* change some GPIO data */
- /* Note: Bit d7 of dir appears to control the LED. So we shut it
- off here. */
- pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
- pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
-
return status;
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
index ffbc6d09610..abaada31e66 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-fx2-cmd.h
@@ -22,32 +22,41 @@
#ifndef _PVRUSB2_FX2_CMD_H_
#define _PVRUSB2_FX2_CMD_H_
-#define FX2CMD_MEM_WRITE_DWORD 0x01
-#define FX2CMD_MEM_READ_DWORD 0x02
+#define FX2CMD_MEM_WRITE_DWORD 0x01u
+#define FX2CMD_MEM_READ_DWORD 0x02u
-#define FX2CMD_MEM_READ_64BYTES 0x28
+#define FX2CMD_MEM_READ_64BYTES 0x28u
-#define FX2CMD_REG_WRITE 0x04
-#define FX2CMD_REG_READ 0x05
-#define FX2CMD_MEMSEL 0x06
+#define FX2CMD_REG_WRITE 0x04u
+#define FX2CMD_REG_READ 0x05u
+#define FX2CMD_MEMSEL 0x06u
-#define FX2CMD_I2C_WRITE 0x08
-#define FX2CMD_I2C_READ 0x09
+#define FX2CMD_I2C_WRITE 0x08u
+#define FX2CMD_I2C_READ 0x09u
-#define FX2CMD_GET_USB_SPEED 0x0b
+#define FX2CMD_GET_USB_SPEED 0x0bu
-#define FX2CMD_STREAMING_ON 0x36
-#define FX2CMD_STREAMING_OFF 0x37
+#define FX2CMD_STREAMING_ON 0x36u
+#define FX2CMD_STREAMING_OFF 0x37u
-#define FX2CMD_FWPOST1 0x52
+#define FX2CMD_FWPOST1 0x52u
-#define FX2CMD_POWER_OFF 0xdc
-#define FX2CMD_POWER_ON 0xde
+#define FX2CMD_POWER_OFF 0xdcu
+#define FX2CMD_POWER_ON 0xdeu
-#define FX2CMD_DEEP_RESET 0xdd
+#define FX2CMD_DEEP_RESET 0xddu
-#define FX2CMD_GET_EEPROM_ADDR 0xeb
-#define FX2CMD_GET_IR_CODE 0xec
+#define FX2CMD_GET_EEPROM_ADDR 0xebu
+#define FX2CMD_GET_IR_CODE 0xecu
+
+#define FX2CMD_HCW_DEMOD_RESETIN 0xf0u
+#define FX2CMD_HCW_DTV_STREAMING_ON 0xf1u
+#define FX2CMD_HCW_DTV_STREAMING_OFF 0xf2u
+
+#define FX2CMD_ONAIR_DTV_STREAMING_ON 0xa0u
+#define FX2CMD_ONAIR_DTV_STREAMING_OFF 0xa1u
+#define FX2CMD_ONAIR_DTV_POWER_ON 0xa2u
+#define FX2CMD_ONAIR_DTV_POWER_OFF 0xa3u
#endif /* _PVRUSB2_FX2_CMD_H_ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index d7a216b41b7..a3fe251d6fd 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -163,6 +163,11 @@ struct pvr2_decoder_ctrl {
#define FW1_STATE_RELOAD 3
#define FW1_STATE_OK 4
+/* What state the device is in if it is a hybrid */
+#define PVR2_PATHWAY_UNKNOWN 0
+#define PVR2_PATHWAY_ANALOG 1
+#define PVR2_PATHWAY_DIGITAL 2
+
typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
#define PVR2_I2C_FUNC_CNT 128
@@ -182,7 +187,6 @@ struct pvr2_hdw {
struct workqueue_struct *workqueue;
struct work_struct workpoll; /* Update driver state */
struct work_struct worki2csync; /* Update i2c clients */
- struct work_struct workinit; /* Driver initialization sequence */
/* Video spigot */
struct pvr2_stream *vid_stream;
@@ -229,17 +233,19 @@ struct pvr2_hdw {
/* Bits of state that describe what is going on with various parts
of the driver. */
+ int state_pathway_ok; /* Pathway config is ok */
int state_encoder_ok; /* Encoder is operational */
int state_encoder_run; /* Encoder is running */
int state_encoder_config; /* Encoder is configured */
int state_encoder_waitok; /* Encoder pre-wait done */
+ int state_encoder_runok; /* Encoder has run for >= .25 sec */
int state_decoder_run; /* Decoder is running */
int state_usbstream_run; /* FX2 is streaming */
int state_decoder_quiescent; /* Decoder idle for > 50msec */
int state_pipeline_config; /* Pipeline is configured */
- int state_pipeline_req; /* Somebody wants to stream */
- int state_pipeline_pause; /* Pipeline must be paused */
- int state_pipeline_idle; /* Pipeline not running */
+ int state_pipeline_req; /* Somebody wants to stream */
+ int state_pipeline_pause; /* Pipeline must be paused */
+ int state_pipeline_idle; /* Pipeline not running */
/* This is the master state of the driver. It is the combined
result of other bits of state. Examining this will indicate the
@@ -247,6 +253,9 @@ struct pvr2_hdw {
PVR2_STATE_xxxx */
unsigned int master_state;
+ /* True if device led is currently on */
+ int led_on;
+
/* True if states must be re-evaluated */
int state_stale;
@@ -259,6 +268,9 @@ struct pvr2_hdw {
/* Timer for measuring encoder pre-wait time */
struct timer_list encoder_wait_timer;
+ /* Timer for measuring encoder minimum run time */
+ struct timer_list encoder_run_timer;
+
/* Place to block while waiting for state changes */
wait_queue_head_t state_wait_data;
@@ -267,6 +279,7 @@ struct pvr2_hdw {
int flag_disconnected; /* flag_ok == 0 due to disconnect */
int flag_init_ok; /* true if structure is fully initialized */
int fw1_state; /* current situation with fw1 */
+ int pathway_state; /* one of PVR2_PATHWAY_xxx */
int flag_decoder_missed;/* We've noticed missing decoder */
int flag_tripped; /* Indicates overall failure to start */
@@ -323,6 +336,11 @@ struct pvr2_hdw {
int v4l_minor_number_vbi;
int v4l_minor_number_radio;
+ /* Bit mask of PVR2_CVAL_INPUT choices which are valid for the hardware */
+ unsigned int input_avail_mask;
+ /* Bit mask of PVR2_CVAL_INPUT choices which are currenly allowed */
+ unsigned int input_allowed_mask;
+
/* Location of eeprom or a negative number if none */
int eeprom_addr;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index 2404053a4d8..0a868888f38 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -43,13 +43,13 @@
static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = NULL};
static DEFINE_MUTEX(pvr2_unit_mtx);
-static int ctlchg = 0;
+static int ctlchg;
static int initusbreset = 1;
-static int procreload = 0;
+static int procreload;
static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };
static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
-static int init_pause_msec = 0;
+static int init_pause_msec;
module_param(ctlchg, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value");
@@ -182,6 +182,7 @@ static const char *control_values_srate[] = {
static const char *control_values_input[] = {
[PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/
+ [PVR2_CVAL_INPUT_DTV] = "dtv",
[PVR2_CVAL_INPUT_RADIO] = "radio",
[PVR2_CVAL_INPUT_SVIDEO] = "s-video",
[PVR2_CVAL_INPUT_COMPOSITE] = "composite",
@@ -215,12 +216,45 @@ static const char *pvr2_state_names[] = {
};
+struct pvr2_fx2cmd_descdef {
+ unsigned char id;
+ unsigned char *desc;
+};
+
+static const struct pvr2_fx2cmd_descdef pvr2_fx2cmd_desc[] = {
+ {FX2CMD_MEM_WRITE_DWORD, "write encoder dword"},
+ {FX2CMD_MEM_READ_DWORD, "read encoder dword"},
+ {FX2CMD_MEM_READ_64BYTES, "read encoder 64bytes"},
+ {FX2CMD_REG_WRITE, "write encoder register"},
+ {FX2CMD_REG_READ, "read encoder register"},
+ {FX2CMD_MEMSEL, "encoder memsel"},
+ {FX2CMD_I2C_WRITE, "i2c write"},
+ {FX2CMD_I2C_READ, "i2c read"},
+ {FX2CMD_GET_USB_SPEED, "get USB speed"},
+ {FX2CMD_STREAMING_ON, "stream on"},
+ {FX2CMD_STREAMING_OFF, "stream off"},
+ {FX2CMD_FWPOST1, "fwpost1"},
+ {FX2CMD_POWER_OFF, "power off"},
+ {FX2CMD_POWER_ON, "power on"},
+ {FX2CMD_DEEP_RESET, "deep reset"},
+ {FX2CMD_GET_EEPROM_ADDR, "get rom addr"},
+ {FX2CMD_GET_IR_CODE, "get IR code"},
+ {FX2CMD_HCW_DEMOD_RESETIN, "hcw demod resetin"},
+ {FX2CMD_HCW_DTV_STREAMING_ON, "hcw dtv stream on"},
+ {FX2CMD_HCW_DTV_STREAMING_OFF, "hcw dtv stream off"},
+ {FX2CMD_ONAIR_DTV_STREAMING_ON, "onair dtv stream on"},
+ {FX2CMD_ONAIR_DTV_STREAMING_OFF, "onair dtv stream off"},
+ {FX2CMD_ONAIR_DTV_POWER_ON, "onair dtv power on"},
+ {FX2CMD_ONAIR_DTV_POWER_OFF, "onair dtv power off"},
+};
+
+
+static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v);
static void pvr2_hdw_state_sched(struct pvr2_hdw *);
static int pvr2_hdw_state_eval(struct pvr2_hdw *);
static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
static void pvr2_hdw_worker_i2c(struct work_struct *work);
static void pvr2_hdw_worker_poll(struct work_struct *work);
-static void pvr2_hdw_worker_init(struct work_struct *work);
static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
static void pvr2_hdw_state_log_state(struct pvr2_hdw *);
@@ -231,6 +265,8 @@ static void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
static void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
static void pvr2_hdw_quiescent_timeout(unsigned long);
static void pvr2_hdw_encoder_wait_timeout(unsigned long);
+static void pvr2_hdw_encoder_run_timeout(unsigned long);
+static int pvr2_issue_simple_cmd(struct pvr2_hdw *,u32);
static int pvr2_send_request_ex(struct pvr2_hdw *hdw,
unsigned int timeout,int probe_fl,
void *write_data,unsigned int write_len,
@@ -367,26 +403,14 @@ static int ctrl_get_input(struct pvr2_ctrl *cptr,int *vp)
return 0;
}
-static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
+static int ctrl_check_input(struct pvr2_ctrl *cptr,int v)
{
- struct pvr2_hdw *hdw = cptr->hdw;
-
- if (hdw->input_val != v) {
- hdw->input_val = v;
- hdw->input_dirty = !0;
- }
+ return ((1 << v) & cptr->hdw->input_allowed_mask) != 0;
+}
- /* Handle side effects - if we switch to a mode that needs the RF
- tuner, then select the right frequency choice as well and mark
- it dirty. */
- if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
- hdw->freqSelector = 0;
- hdw->freqDirty = !0;
- } else if (hdw->input_val == PVR2_CVAL_INPUT_TV) {
- hdw->freqSelector = 1;
- hdw->freqDirty = !0;
- }
- return 0;
+static int ctrl_set_input(struct pvr2_ctrl *cptr,int m,int v)
+{
+ return pvr2_hdw_set_input(cptr->hdw,v);
}
static int ctrl_isdirty_input(struct pvr2_ctrl *cptr)
@@ -803,6 +827,7 @@ static const struct pvr2_ctl_info control_defs[] = {
.name = "input",
.internal_id = PVR2_CID_INPUT,
.default_value = PVR2_CVAL_INPUT_TV,
+ .check_value = ctrl_check_input,
DEFREF(input),
DEFENUM(control_values_input),
},{
@@ -982,7 +1007,7 @@ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
/* Set the currently tuned frequency and account for all possible
driver-core side effects of this action. */
-void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val)
+static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *hdw,unsigned long val)
{
if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
if (hdw->freqSelector) {
@@ -1195,6 +1220,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
time we configure the encoder, then we'll fully configure it. */
hdw->enc_cur_valid = 0;
+ /* Encoder is about to be reset so note that as far as we're
+ concerned now, the encoder has never been run. */
+ del_timer_sync(&hdw->encoder_run_timer);
+ if (hdw->state_encoder_runok) {
+ hdw->state_encoder_runok = 0;
+ trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
+ }
+
/* First prepare firmware loading */
ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
@@ -1212,19 +1245,14 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
- LOCK_TAKE(hdw->ctl_lock); do {
- hdw->cmd_buffer[0] = FX2CMD_FWPOST1;
- ret |= pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
- hdw->cmd_buffer[0] = FX2CMD_MEMSEL;
- hdw->cmd_buffer[1] = 0;
- ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0);
- } while (0); LOCK_GIVE(hdw->ctl_lock);
+ ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_FWPOST1);
+ ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"firmware2 upload prep failed, ret=%d",ret);
release_firmware(fw_entry);
- return ret;
+ goto done;
}
/* Now send firmware */
@@ -1237,7 +1265,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
" must be a multiple of %zu bytes",
fw_files[fwidx],sizeof(u32));
release_firmware(fw_entry);
- return -1;
+ ret = -EINVAL;
+ goto done;
}
fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
@@ -1245,7 +1274,8 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
release_firmware(fw_entry);
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"failed to allocate memory for firmware2 upload");
- return -ENOMEM;
+ ret = -ENOMEM;
+ goto done;
}
pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
@@ -1276,23 +1306,27 @@ int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"firmware2 upload transfer failure");
- return ret;
+ goto done;
}
/* Finish upload */
ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
- LOCK_TAKE(hdw->ctl_lock); do {
- hdw->cmd_buffer[0] = FX2CMD_MEMSEL;
- hdw->cmd_buffer[1] = 0;
- ret |= pvr2_send_request(hdw,hdw->cmd_buffer,2,NULL,0);
- } while (0); LOCK_GIVE(hdw->ctl_lock);
+ ret |= pvr2_issue_simple_cmd(hdw,FX2CMD_MEMSEL | (1 << 8) | (0 << 16));
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
"firmware2 upload post-proc failure");
}
+
+ done:
+ if (hdw->hdw_desc->signal_routing_scheme ==
+ PVR2_ROUTING_SCHEME_GOTVIEW) {
+ /* Ensure that GPIO 11 is set to output for GOTVIEW
+ hardware. */
+ pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
+ }
return ret;
}
@@ -1364,11 +1398,6 @@ int pvr2_hdw_untrip(struct pvr2_hdw *hdw)
}
-const char *pvr2_hdw_get_state_name(unsigned int id)
-{
- if (id >= ARRAY_SIZE(pvr2_state_names)) return NULL;
- return pvr2_state_names[id];
-}
int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
@@ -1495,7 +1524,7 @@ struct pvr2_std_hack {
default - which can always be overridden explicitly - and if the user
has otherwise named a default then that default will always be used in
place of this table. */
-const static struct pvr2_std_hack std_eeprom_maps[] = {
+static const struct pvr2_std_hack std_eeprom_maps[] = {
{ /* PAL(B/G) */
.pat = V4L2_STD_B|V4L2_STD_GH,
.std = V4L2_STD_PAL_B|V4L2_STD_PAL_B1|V4L2_STD_PAL_G,
@@ -1712,6 +1741,13 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
if (!pvr2_hdw_dev_ok(hdw)) return;
+ if (hdw->hdw_desc->signal_routing_scheme ==
+ PVR2_ROUTING_SCHEME_GOTVIEW) {
+ /* Ensure that GPIO 11 is set to output for GOTVIEW
+ hardware. */
+ pvr2_hdw_gpio_chg_dir(hdw,(1 << 11),~0);
+ }
+
pvr2_hdw_commit_setup(hdw);
hdw->vid_stream = pvr2_stream_create();
@@ -1805,12 +1841,37 @@ static void pvr2_hdw_setup(struct pvr2_hdw *hdw)
}
-/* Create and return a structure for interacting with the underlying
- hardware */
+/* Perform second stage initialization. Set callback pointer first so that
+ we can avoid a possible initialization race (if the kernel thread runs
+ before the callback has been set). */
+int pvr2_hdw_initialize(struct pvr2_hdw *hdw,
+ void (*callback_func)(void *),
+ void *callback_data)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ if (hdw->flag_disconnected) {
+ /* Handle a race here: If we're already
+ disconnected by this point, then give up. If we
+ get past this then we'll remain connected for
+ the duration of initialization since the entire
+ initialization sequence is now protected by the
+ big_lock. */
+ break;
+ }
+ hdw->state_data = callback_data;
+ hdw->state_func = callback_func;
+ pvr2_hdw_setup(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return hdw->flag_init_ok;
+}
+
+
+/* Create, set up, and return a structure for interacting with the
+ underlying hardware. */
struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
const struct usb_device_id *devid)
{
- unsigned int idx,cnt1,cnt2;
+ unsigned int idx,cnt1,cnt2,m;
struct pvr2_hdw *hdw;
int valid_std_mask;
struct pvr2_ctrl *cptr;
@@ -1834,6 +1895,10 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->encoder_wait_timer.data = (unsigned long)hdw;
hdw->encoder_wait_timer.function = pvr2_hdw_encoder_wait_timeout;
+ init_timer(&hdw->encoder_run_timer);
+ hdw->encoder_run_timer.data = (unsigned long)hdw;
+ hdw->encoder_run_timer.function = pvr2_hdw_encoder_run_timeout;
+
hdw->master_state = PVR2_STATE_DEAD;
init_waitqueue_head(&hdw->state_wait_data);
@@ -1841,6 +1906,26 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->tuner_signal_stale = !0;
cx2341x_fill_defaults(&hdw->enc_ctl_state);
+ /* Calculate which inputs are OK */
+ m = 0;
+ if (hdw_desc->flag_has_analogtuner) m |= 1 << PVR2_CVAL_INPUT_TV;
+ if (hdw_desc->digital_control_scheme != PVR2_DIGITAL_SCHEME_NONE) {
+ m |= 1 << PVR2_CVAL_INPUT_DTV;
+ }
+ if (hdw_desc->flag_has_svideo) m |= 1 << PVR2_CVAL_INPUT_SVIDEO;
+ if (hdw_desc->flag_has_composite) m |= 1 << PVR2_CVAL_INPUT_COMPOSITE;
+ if (hdw_desc->flag_has_fmradio) m |= 1 << PVR2_CVAL_INPUT_RADIO;
+ hdw->input_avail_mask = m;
+ hdw->input_allowed_mask = hdw->input_avail_mask;
+
+ /* If not a hybrid device, pathway_state never changes. So
+ initialize it here to what it should forever be. */
+ if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_DTV))) {
+ hdw->pathway_state = PVR2_PATHWAY_ANALOG;
+ } else if (!(hdw->input_avail_mask & (1 << PVR2_CVAL_INPUT_TV))) {
+ hdw->pathway_state = PVR2_PATHWAY_DIGITAL;
+ }
+
hdw->control_cnt = CTRLDEF_COUNT;
hdw->control_cnt += MPEGDEF_COUNT;
hdw->controls = kzalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
@@ -1858,6 +1943,15 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
cptr = hdw->controls + idx;
cptr->info = control_defs+idx;
}
+
+ /* Ensure that default input choice is a valid one. */
+ m = hdw->input_avail_mask;
+ if (m) for (idx = 0; idx < (sizeof(m) << 3); idx++) {
+ if (!((1 << idx) & m)) continue;
+ hdw->input_val = idx;
+ break;
+ }
+
/* Define and configure additional controls from cx2341x module. */
hdw->mpeg_ctrl_info = kzalloc(
sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL);
@@ -1981,7 +2075,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
hdw->workqueue = create_singlethread_workqueue(hdw->name);
INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll);
INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c);
- INIT_WORK(&hdw->workinit,pvr2_hdw_worker_init);
pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
hdw->unit_number,hdw->name);
@@ -2003,11 +2096,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
mutex_init(&hdw->ctl_lock_mutex);
mutex_init(&hdw->big_lock_mutex);
- queue_work(hdw->workqueue,&hdw->workinit);
return hdw;
fail:
if (hdw) {
del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->encoder_run_timer);
del_timer_sync(&hdw->encoder_wait_timer);
if (hdw->workqueue) {
flush_workqueue(hdw->workqueue);
@@ -2064,13 +2157,14 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
{
if (!hdw) return;
pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
- del_timer_sync(&hdw->quiescent_timer);
- del_timer_sync(&hdw->encoder_wait_timer);
if (hdw->workqueue) {
flush_workqueue(hdw->workqueue);
destroy_workqueue(hdw->workqueue);
hdw->workqueue = NULL;
}
+ del_timer_sync(&hdw->quiescent_timer);
+ del_timer_sync(&hdw->encoder_run_timer);
+ del_timer_sync(&hdw->encoder_wait_timer);
if (hdw->fw_buffer) {
kfree(hdw->fw_buffer);
hdw->fw_buffer = NULL;
@@ -2352,6 +2446,18 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
}
}
+ if (hdw->input_dirty && hdw->state_pathway_ok &&
+ (((hdw->input_val == PVR2_CVAL_INPUT_DTV) ?
+ PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG) !=
+ hdw->pathway_state)) {
+ /* Change of mode being asked for... */
+ hdw->state_pathway_ok = 0;
+ trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
+ }
+ if (!hdw->state_pathway_ok) {
+ /* Can't commit anything until pathway is ok. */
+ return 0;
+ }
/* If any of the below has changed, then we can't do the update
while the pipeline is running. Pipeline must be paused first
and decoder -> encoder connection be made quiescent before we
@@ -2405,12 +2511,28 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
hdw->active_stream_type = hdw->desired_stream_type;
}
+ if (hdw->hdw_desc->signal_routing_scheme ==
+ PVR2_ROUTING_SCHEME_GOTVIEW) {
+ u32 b;
+ /* Handle GOTVIEW audio switching */
+ pvr2_hdw_gpio_get_out(hdw,&b);
+ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+ /* Set GPIO 11 */
+ pvr2_hdw_gpio_chg_out(hdw,(1 << 11),~0);
+ } else {
+ /* Clear GPIO 11 */
+ pvr2_hdw_gpio_chg_out(hdw,(1 << 11),0);
+ }
+ }
+
/* Now execute i2c core update */
pvr2_i2c_core_sync(hdw);
- if (hdw->state_encoder_run) {
- /* If encoder isn't running, then this will get worked out
- later when we start the encoder. */
+ if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) &&
+ hdw->state_encoder_run) {
+ /* If encoder isn't running or it can't be touched, then
+ this will get worked out later when we start the
+ encoder. */
if (pvr2_encoder_adjust(hdw) < 0) return !0;
}
@@ -2453,15 +2575,6 @@ static void pvr2_hdw_worker_poll(struct work_struct *work)
}
-static void pvr2_hdw_worker_init(struct work_struct *work)
-{
- struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,workinit);
- LOCK_TAKE(hdw->big_lock); do {
- pvr2_hdw_setup(hdw);
- } while (0); LOCK_GIVE(hdw->big_lock);
-}
-
-
static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
{
return wait_event_interruptible(
@@ -2471,17 +2584,6 @@ static int pvr2_hdw_wait(struct pvr2_hdw *hdw,int state)
}
-void pvr2_hdw_set_state_callback(struct pvr2_hdw *hdw,
- void (*callback_func)(void *),
- void *callback_data)
-{
- LOCK_TAKE(hdw->big_lock); do {
- hdw->state_data = callback_data;
- hdw->state_func = callback_func;
- } while (0); LOCK_GIVE(hdw->big_lock);
-}
-
-
/* Return name for this driver instance */
const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
{
@@ -3050,6 +3152,67 @@ int pvr2_send_request(struct pvr2_hdw *hdw,
read_data,read_len);
}
+
+static int pvr2_issue_simple_cmd(struct pvr2_hdw *hdw,u32 cmdcode)
+{
+ int ret;
+ unsigned int cnt = 1;
+ unsigned int args = 0;
+ LOCK_TAKE(hdw->ctl_lock);
+ hdw->cmd_buffer[0] = cmdcode & 0xffu;
+ args = (cmdcode >> 8) & 0xffu;
+ args = (args > 2) ? 2 : args;
+ if (args) {
+ cnt += args;
+ hdw->cmd_buffer[1] = (cmdcode >> 16) & 0xffu;
+ if (args > 1) {
+ hdw->cmd_buffer[2] = (cmdcode >> 24) & 0xffu;
+ }
+ }
+ if (pvrusb2_debug & PVR2_TRACE_INIT) {
+ unsigned int idx;
+ unsigned int ccnt,bcnt;
+ char tbuf[50];
+ cmdcode &= 0xffu;
+ bcnt = 0;
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ "Sending FX2 command 0x%x",cmdcode);
+ bcnt += ccnt;
+ for (idx = 0; idx < ARRAY_SIZE(pvr2_fx2cmd_desc); idx++) {
+ if (pvr2_fx2cmd_desc[idx].id == cmdcode) {
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ " \"%s\"",
+ pvr2_fx2cmd_desc[idx].desc);
+ bcnt += ccnt;
+ break;
+ }
+ }
+ if (args) {
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ " (%u",hdw->cmd_buffer[1]);
+ bcnt += ccnt;
+ if (args > 1) {
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ ",%u",hdw->cmd_buffer[2]);
+ bcnt += ccnt;
+ }
+ ccnt = scnprintf(tbuf+bcnt,
+ sizeof(tbuf)-bcnt,
+ ")");
+ bcnt += ccnt;
+ }
+ pvr2_trace(PVR2_TRACE_INIT,"%.*s",bcnt,tbuf);
+ }
+ ret = pvr2_send_request(hdw,hdw->cmd_buffer,cnt,NULL,0);
+ LOCK_GIVE(hdw->ctl_lock);
+ return ret;
+}
+
+
int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
{
int ret;
@@ -3157,25 +3320,19 @@ void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
{
- int status;
- LOCK_TAKE(hdw->ctl_lock); do {
- pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset");
- hdw->cmd_buffer[0] = FX2CMD_DEEP_RESET;
- status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
- } while (0); LOCK_GIVE(hdw->ctl_lock);
- return status;
+ return pvr2_issue_simple_cmd(hdw,FX2CMD_DEEP_RESET);
}
int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
{
- int status;
- LOCK_TAKE(hdw->ctl_lock); do {
- pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup");
- hdw->cmd_buffer[0] = FX2CMD_POWER_ON;
- status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
- } while (0); LOCK_GIVE(hdw->ctl_lock);
- return status;
+ return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_ON);
+}
+
+
+int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw)
+{
+ return pvr2_issue_simple_cmd(hdw,FX2CMD_POWER_OFF);
}
@@ -3200,16 +3357,173 @@ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
}
+static int pvr2_hdw_cmd_hcw_demod_reset(struct pvr2_hdw *hdw, int onoff)
+{
+ hdw->flag_ok = !0;
+ return pvr2_issue_simple_cmd(hdw,
+ FX2CMD_HCW_DEMOD_RESETIN |
+ (1 << 8) |
+ ((onoff ? 1 : 0) << 16));
+}
+
+
+static int pvr2_hdw_cmd_onair_fe_power_ctrl(struct pvr2_hdw *hdw, int onoff)
+{
+ hdw->flag_ok = !0;
+ return pvr2_issue_simple_cmd(hdw,(onoff ?
+ FX2CMD_ONAIR_DTV_POWER_ON :
+ FX2CMD_ONAIR_DTV_POWER_OFF));
+}
+
+
+static int pvr2_hdw_cmd_onair_digital_path_ctrl(struct pvr2_hdw *hdw,
+ int onoff)
+{
+ return pvr2_issue_simple_cmd(hdw,(onoff ?
+ FX2CMD_ONAIR_DTV_STREAMING_ON :
+ FX2CMD_ONAIR_DTV_STREAMING_OFF));
+}
+
+
+static void pvr2_hdw_cmd_modeswitch(struct pvr2_hdw *hdw,int digitalFl)
+{
+ int cmode;
+ /* Compare digital/analog desired setting with current setting. If
+ they don't match, fix it... */
+ cmode = (digitalFl ? PVR2_PATHWAY_DIGITAL : PVR2_PATHWAY_ANALOG);
+ if (cmode == hdw->pathway_state) {
+ /* They match; nothing to do */
+ return;
+ }
+
+ switch (hdw->hdw_desc->digital_control_scheme) {
+ case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
+ pvr2_hdw_cmd_hcw_demod_reset(hdw,digitalFl);
+ if (cmode == PVR2_PATHWAY_ANALOG) {
+ /* If moving to analog mode, also force the decoder
+ to reset. If no decoder is attached, then it's
+ ok to ignore this because if/when the decoder
+ attaches, it will reset itself at that time. */
+ pvr2_hdw_cmd_decoder_reset(hdw);
+ }
+ break;
+ case PVR2_DIGITAL_SCHEME_ONAIR:
+ /* Supposedly we should always have the power on whether in
+ digital or analog mode. But for now do what appears to
+ work... */
+ pvr2_hdw_cmd_onair_fe_power_ctrl(hdw,digitalFl);
+ break;
+ default: break;
+ }
+
+ pvr2_hdw_untrip_unlocked(hdw);
+ hdw->pathway_state = cmode;
+}
+
+
+void pvr2_led_ctrl_hauppauge(struct pvr2_hdw *hdw, int onoff)
+{
+ /* change some GPIO data
+ *
+ * note: bit d7 of dir appears to control the LED,
+ * so we shut it off here.
+ *
+ */
+ if (onoff) {
+ pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000481);
+ } else {
+ pvr2_hdw_gpio_chg_dir(hdw, 0xffffffff, 0x00000401);
+ }
+ pvr2_hdw_gpio_chg_out(hdw, 0xffffffff, 0x00000000);
+}
+
+
+typedef void (*led_method_func)(struct pvr2_hdw *,int);
+
+static led_method_func led_methods[] = {
+ [PVR2_LED_SCHEME_HAUPPAUGE] = pvr2_led_ctrl_hauppauge,
+};
+
+
+/* Toggle LED */
+static void pvr2_led_ctrl(struct pvr2_hdw *hdw,int onoff)
+{
+ unsigned int scheme_id;
+ led_method_func fp;
+
+ if ((!onoff) == (!hdw->led_on)) return;
+
+ hdw->led_on = onoff != 0;
+
+ scheme_id = hdw->hdw_desc->led_scheme;
+ if (scheme_id < ARRAY_SIZE(led_methods)) {
+ fp = led_methods[scheme_id];
+ } else {
+ fp = NULL;
+ }
+
+ if (fp) (*fp)(hdw,onoff);
+}
+
+
/* Stop / start video stream transport */
static int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
{
- int status;
- LOCK_TAKE(hdw->ctl_lock); do {
- hdw->cmd_buffer[0] =
- (runFl ? FX2CMD_STREAMING_ON : FX2CMD_STREAMING_OFF);
- status = pvr2_send_request(hdw,hdw->cmd_buffer,1,NULL,0);
- } while (0); LOCK_GIVE(hdw->ctl_lock);
- return status;
+ int ret;
+
+ /* If we're in analog mode, then just issue the usual analog
+ command. */
+ if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
+ return pvr2_issue_simple_cmd(hdw,
+ (runFl ?
+ FX2CMD_STREAMING_ON :
+ FX2CMD_STREAMING_OFF));
+ /*Note: Not reached */
+ }
+
+ if (hdw->pathway_state != PVR2_PATHWAY_DIGITAL) {
+ /* Whoops, we don't know what mode we're in... */
+ return -EINVAL;
+ }
+
+ /* To get here we have to be in digital mode. The mechanism here
+ is unfortunately different for different vendors. So we switch
+ on the device's digital scheme attribute in order to figure out
+ what to do. */
+ switch (hdw->hdw_desc->digital_control_scheme) {
+ case PVR2_DIGITAL_SCHEME_HAUPPAUGE:
+ return pvr2_issue_simple_cmd(hdw,
+ (runFl ?
+ FX2CMD_HCW_DTV_STREAMING_ON :
+ FX2CMD_HCW_DTV_STREAMING_OFF));
+ case PVR2_DIGITAL_SCHEME_ONAIR:
+ ret = pvr2_issue_simple_cmd(hdw,
+ (runFl ?
+ FX2CMD_STREAMING_ON :
+ FX2CMD_STREAMING_OFF));
+ if (ret) return ret;
+ return pvr2_hdw_cmd_onair_digital_path_ctrl(hdw,runFl);
+ default:
+ return -EINVAL;
+ }
+}
+
+
+/* Evaluate whether or not state_pathway_ok can change */
+static int state_eval_pathway_ok(struct pvr2_hdw *hdw)
+{
+ if (hdw->state_pathway_ok) {
+ /* Nothing to do if pathway is already ok */
+ return 0;
+ }
+ if (!hdw->state_pipeline_idle) {
+ /* Not allowed to change anything if pipeline is not idle */
+ return 0;
+ }
+ pvr2_hdw_cmd_modeswitch(hdw,hdw->input_val == PVR2_CVAL_INPUT_DTV);
+ hdw->state_pathway_ok = !0;
+ trace_stbit("state_pathway_ok",hdw->state_pathway_ok);
+ return !0;
}
@@ -3222,6 +3536,12 @@ static int state_eval_encoder_ok(struct pvr2_hdw *hdw)
if (hdw->state_encoder_config) return 0;
if (hdw->state_decoder_run) return 0;
if (hdw->state_usbstream_run) return 0;
+ if (hdw->pathway_state == PVR2_PATHWAY_DIGITAL) {
+ if (!hdw->hdw_desc->flag_digital_requires_cx23416) return 0;
+ } else if (hdw->pathway_state != PVR2_PATHWAY_ANALOG) {
+ return 0;
+ }
+
if (pvr2_upload_firmware2(hdw) < 0) {
hdw->flag_tripped = !0;
trace_stbit("flag_tripped",hdw->flag_tripped);
@@ -3247,7 +3567,9 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw)
/* paranoia - solve race if timer just completed */
del_timer_sync(&hdw->encoder_wait_timer);
} else {
- if (!hdw->state_encoder_ok ||
+ if (!hdw->state_pathway_ok ||
+ (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
+ !hdw->state_encoder_ok ||
!hdw->state_pipeline_idle ||
hdw->state_pipeline_pause ||
!hdw->state_pipeline_req ||
@@ -3296,20 +3618,116 @@ static int state_eval_encoder_config(struct pvr2_hdw *hdw)
}
+/* Return true if the encoder should not be running. */
+static int state_check_disable_encoder_run(struct pvr2_hdw *hdw)
+{
+ if (!hdw->state_encoder_ok) {
+ /* Encoder isn't healthy at the moment, so stop it. */
+ return !0;
+ }
+ if (!hdw->state_pathway_ok) {
+ /* Mode is not understood at the moment (i.e. it wants to
+ change), so encoder must be stopped. */
+ return !0;
+ }
+
+ switch (hdw->pathway_state) {
+ case PVR2_PATHWAY_ANALOG:
+ if (!hdw->state_decoder_run) {
+ /* We're in analog mode and the decoder is not
+ running; thus the encoder should be stopped as
+ well. */
+ return !0;
+ }
+ break;
+ case PVR2_PATHWAY_DIGITAL:
+ if (hdw->state_encoder_runok) {
+ /* This is a funny case. We're in digital mode so
+ really the encoder should be stopped. However
+ if it really is running, only kill it after
+ runok has been set. This gives a chance for the
+ onair quirk to function (encoder must run
+ briefly first, at least once, before onair
+ digital streaming can work). */
+ return !0;
+ }
+ break;
+ default:
+ /* Unknown mode; so encoder should be stopped. */
+ return !0;
+ }
+
+ /* If we get here, we haven't found a reason to stop the
+ encoder. */
+ return 0;
+}
+
+
+/* Return true if the encoder should be running. */
+static int state_check_enable_encoder_run(struct pvr2_hdw *hdw)
+{
+ if (!hdw->state_encoder_ok) {
+ /* Don't run the encoder if it isn't healthy... */
+ return 0;
+ }
+ if (!hdw->state_pathway_ok) {
+ /* Don't run the encoder if we don't (yet) know what mode
+ we need to be in... */
+ return 0;
+ }
+
+ switch (hdw->pathway_state) {
+ case PVR2_PATHWAY_ANALOG:
+ if (hdw->state_decoder_run) {
+ /* In analog mode, if the decoder is running, then
+ run the encoder. */
+ return !0;
+ }
+ break;
+ case PVR2_PATHWAY_DIGITAL:
+ if ((hdw->hdw_desc->digital_control_scheme ==
+ PVR2_DIGITAL_SCHEME_ONAIR) &&
+ !hdw->state_encoder_runok) {
+ /* This is a quirk. OnAir hardware won't stream
+ digital until the encoder has been run at least
+ once, for a minimal period of time (empiricially
+ measured to be 1/4 second). So if we're on
+ OnAir hardware and the encoder has never been
+ run at all, then start the encoder. Normal
+ state machine logic in the driver will
+ automatically handle the remaining bits. */
+ return !0;
+ }
+ break;
+ default:
+ /* For completeness (unknown mode; encoder won't run ever) */
+ break;
+ }
+ /* If we get here, then we haven't found any reason to run the
+ encoder, so don't run it. */
+ return 0;
+}
+
+
/* Evaluate whether or not state_encoder_run can change */
static int state_eval_encoder_run(struct pvr2_hdw *hdw)
{
if (hdw->state_encoder_run) {
+ if (!state_check_disable_encoder_run(hdw)) return 0;
if (hdw->state_encoder_ok) {
- if (hdw->state_decoder_run) return 0;
+ del_timer_sync(&hdw->encoder_run_timer);
if (pvr2_encoder_stop(hdw) < 0) return !0;
}
hdw->state_encoder_run = 0;
} else {
- if (!hdw->state_encoder_ok) return 0;
- if (!hdw->state_decoder_run) return 0;
+ if (!state_check_enable_encoder_run(hdw)) return 0;
if (pvr2_encoder_start(hdw) < 0) return !0;
hdw->state_encoder_run = !0;
+ if (!hdw->state_encoder_runok) {
+ hdw->encoder_run_timer.expires =
+ jiffies + (HZ*250/1000);
+ add_timer(&hdw->encoder_run_timer);
+ }
}
trace_stbit("state_encoder_run",hdw->state_encoder_run);
return !0;
@@ -3338,13 +3756,27 @@ static void pvr2_hdw_encoder_wait_timeout(unsigned long data)
}
+/* Timeout function for encoder run timer. */
+static void pvr2_hdw_encoder_run_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ if (!hdw->state_encoder_runok) {
+ hdw->state_encoder_runok = !0;
+ trace_stbit("state_encoder_runok",hdw->state_encoder_runok);
+ hdw->state_stale = !0;
+ queue_work(hdw->workqueue,&hdw->workpoll);
+ }
+}
+
+
/* Evaluate whether or not state_decoder_run can change */
static int state_eval_decoder_run(struct pvr2_hdw *hdw)
{
if (hdw->state_decoder_run) {
if (hdw->state_encoder_ok) {
if (hdw->state_pipeline_req &&
- !hdw->state_pipeline_pause) return 0;
+ !hdw->state_pipeline_pause &&
+ hdw->state_pathway_ok) return 0;
}
if (!hdw->flag_decoder_missed) {
pvr2_decoder_enable(hdw,0);
@@ -3377,7 +3809,9 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw)
hopefully further stabilize the encoder. */
return 0;
}
- if (!hdw->state_pipeline_req ||
+ if (!hdw->state_pathway_ok ||
+ (hdw->pathway_state != PVR2_PATHWAY_ANALOG) ||
+ !hdw->state_pipeline_req ||
hdw->state_pipeline_pause ||
!hdw->state_pipeline_config ||
!hdw->state_encoder_config ||
@@ -3398,16 +3832,43 @@ static int state_eval_decoder_run(struct pvr2_hdw *hdw)
static int state_eval_usbstream_run(struct pvr2_hdw *hdw)
{
if (hdw->state_usbstream_run) {
- if (hdw->state_encoder_ok) {
- if (hdw->state_encoder_run) return 0;
+ int fl = !0;
+ if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
+ fl = (hdw->state_encoder_ok &&
+ hdw->state_encoder_run);
+ } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
+ (hdw->hdw_desc->flag_digital_requires_cx23416)) {
+ fl = hdw->state_encoder_ok;
+ }
+ if (fl &&
+ hdw->state_pipeline_req &&
+ !hdw->state_pipeline_pause &&
+ hdw->state_pathway_ok) {
+ return 0;
}
pvr2_hdw_cmd_usbstream(hdw,0);
hdw->state_usbstream_run = 0;
} else {
- if (!hdw->state_encoder_ok ||
- !hdw->state_encoder_run ||
- !hdw->state_pipeline_req ||
- hdw->state_pipeline_pause) return 0;
+ if (!hdw->state_pipeline_req ||
+ hdw->state_pipeline_pause ||
+ !hdw->state_pathway_ok) return 0;
+ if (hdw->pathway_state == PVR2_PATHWAY_ANALOG) {
+ if (!hdw->state_encoder_ok ||
+ !hdw->state_encoder_run) return 0;
+ } else if ((hdw->pathway_state == PVR2_PATHWAY_DIGITAL) &&
+ (hdw->hdw_desc->flag_digital_requires_cx23416)) {
+ if (!hdw->state_encoder_ok) return 0;
+ if (hdw->state_encoder_run) return 0;
+ if (hdw->hdw_desc->digital_control_scheme ==
+ PVR2_DIGITAL_SCHEME_ONAIR) {
+ /* OnAir digital receivers won't stream
+ unless the analog encoder has run first.
+ Why? I have no idea. But don't even
+ try until we know the analog side is
+ known to have run. */
+ if (!hdw->state_encoder_runok) return 0;
+ }
+ }
if (pvr2_hdw_cmd_usbstream(hdw,!0) < 0) return 0;
hdw->state_usbstream_run = !0;
}
@@ -3453,7 +3914,8 @@ static int state_update_pipeline_state(struct pvr2_hdw *hdw)
typedef int (*state_eval_func)(struct pvr2_hdw *);
/* Set of functions to be run to evaluate various states in the driver. */
-const static state_eval_func eval_funcs[] = {
+static const state_eval_func eval_funcs[] = {
+ state_eval_pathway_ok,
state_eval_pipeline_config,
state_eval_encoder_ok,
state_eval_encoder_config,
@@ -3501,6 +3963,34 @@ static int pvr2_hdw_state_update(struct pvr2_hdw *hdw)
}
+static unsigned int print_input_mask(unsigned int msk,
+ char *buf,unsigned int acnt)
+{
+ unsigned int idx,ccnt;
+ unsigned int tcnt = 0;
+ for (idx = 0; idx < ARRAY_SIZE(control_values_input); idx++) {
+ if (!((1 << idx) & msk)) continue;
+ ccnt = scnprintf(buf+tcnt,
+ acnt-tcnt,
+ "%s%s",
+ (tcnt ? ", " : ""),
+ control_values_input[idx]);
+ tcnt += ccnt;
+ }
+ return tcnt;
+}
+
+
+static const char *pvr2_pathway_state_name(int id)
+{
+ switch (id) {
+ case PVR2_PATHWAY_ANALOG: return "analog";
+ case PVR2_PATHWAY_DIGITAL: return "digital";
+ default: return "unknown";
+ }
+}
+
+
static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
char *buf,unsigned int acnt)
{
@@ -3508,13 +3998,15 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
case 0:
return scnprintf(
buf,acnt,
- "driver:%s%s%s%s%s",
+ "driver:%s%s%s%s%s <mode=%s>",
(hdw->flag_ok ? " <ok>" : " <fail>"),
(hdw->flag_init_ok ? " <init>" : " <uninitialized>"),
(hdw->flag_disconnected ? " <disconnected>" :
" <connected>"),
(hdw->flag_tripped ? " <tripped>" : ""),
- (hdw->flag_decoder_missed ? " <no decoder>" : ""));
+ (hdw->flag_decoder_missed ? " <no decoder>" : ""),
+ pvr2_pathway_state_name(hdw->pathway_state));
+
case 1:
return scnprintf(
buf,acnt,
@@ -3527,7 +4019,7 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
case 2:
return scnprintf(
buf,acnt,
- "worker:%s%s%s%s%s%s",
+ "worker:%s%s%s%s%s%s%s",
(hdw->state_decoder_run ?
" <decode:run>" :
(hdw->state_decoder_quiescent ?
@@ -3537,20 +4029,65 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
(hdw->state_encoder_ok ?
"" : " <encode:init>"),
(hdw->state_encoder_run ?
- " <encode:run>" : " <encode:stop>"),
+ (hdw->state_encoder_runok ?
+ " <encode:run>" :
+ " <encode:firstrun>") :
+ (hdw->state_encoder_runok ?
+ " <encode:stop>" :
+ " <encode:virgin>")),
(hdw->state_encoder_config ?
" <encode:configok>" :
(hdw->state_encoder_waitok ?
- "" : " <encode:wait>")),
+ "" : " <encode:waitok>")),
(hdw->state_usbstream_run ?
- " <usb:run>" : " <usb:stop>"));
- break;
+ " <usb:run>" : " <usb:stop>"),
+ (hdw->state_pathway_ok ?
+ " <pathway:ok>" : ""));
case 3:
return scnprintf(
buf,acnt,
"state: %s",
pvr2_get_state_name(hdw->master_state));
- break;
+ case 4: {
+ unsigned int tcnt = 0;
+ unsigned int ccnt;
+
+ ccnt = scnprintf(buf,
+ acnt,
+ "Hardware supported inputs: ");
+ tcnt += ccnt;
+ tcnt += print_input_mask(hdw->input_avail_mask,
+ buf+tcnt,
+ acnt-tcnt);
+ if (hdw->input_avail_mask != hdw->input_allowed_mask) {
+ ccnt = scnprintf(buf+tcnt,
+ acnt-tcnt,
+ "; allowed inputs: ");
+ tcnt += ccnt;
+ tcnt += print_input_mask(hdw->input_allowed_mask,
+ buf+tcnt,
+ acnt-tcnt);
+ }
+ return tcnt;
+ }
+ case 5: {
+ struct pvr2_stream_stats stats;
+ if (!hdw->vid_stream) break;
+ pvr2_stream_get_stats(hdw->vid_stream,
+ &stats,
+ 0);
+ return scnprintf(
+ buf,acnt,
+ "Bytes streamed=%u"
+ " URBs: queued=%u idle=%u ready=%u"
+ " processed=%u failed=%u",
+ stats.bytes_processed,
+ stats.buffers_in_queue,
+ stats.buffers_in_idle,
+ stats.buffers_in_ready,
+ stats.buffers_processed,
+ stats.buffers_failed);
+ }
default: break;
}
return 0;
@@ -3596,6 +4133,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
unsigned int st;
int state_updated = 0;
int callback_flag = 0;
+ int analog_mode;
pvr2_trace(PVR2_TRACE_STBITS,
"Drive state check START");
@@ -3606,18 +4144,23 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
/* Process all state and get back over disposition */
state_updated = pvr2_hdw_state_update(hdw);
+ analog_mode = (hdw->pathway_state != PVR2_PATHWAY_DIGITAL);
+
/* Update master state based upon all other states. */
if (!hdw->flag_ok) {
st = PVR2_STATE_DEAD;
} else if (hdw->fw1_state != FW1_STATE_OK) {
st = PVR2_STATE_COLD;
- } else if (!hdw->state_encoder_ok) {
+ } else if ((analog_mode ||
+ hdw->hdw_desc->flag_digital_requires_cx23416) &&
+ !hdw->state_encoder_ok) {
st = PVR2_STATE_WARM;
- } else if (hdw->flag_tripped || hdw->flag_decoder_missed) {
+ } else if (hdw->flag_tripped ||
+ (analog_mode && hdw->flag_decoder_missed)) {
st = PVR2_STATE_ERROR;
- } else if (hdw->state_encoder_run &&
- hdw->state_decoder_run &&
- hdw->state_usbstream_run) {
+ } else if (hdw->state_usbstream_run &&
+ (!analog_mode ||
+ (hdw->state_encoder_run && hdw->state_decoder_run))) {
st = PVR2_STATE_RUN;
} else {
st = PVR2_STATE_READY;
@@ -3627,6 +4170,7 @@ static int pvr2_hdw_state_eval(struct pvr2_hdw *hdw)
"Device state change from %s to %s",
pvr2_get_state_name(hdw->master_state),
pvr2_get_state_name(st));
+ pvr2_led_ctrl(hdw,st == PVR2_STATE_RUN);
hdw->master_state = st;
state_updated = !0;
callback_flag = !0;
@@ -3656,47 +4200,6 @@ static void pvr2_hdw_state_sched(struct pvr2_hdw *hdw)
}
-void pvr2_hdw_get_debug_info_unlocked(const struct pvr2_hdw *hdw,
- struct pvr2_hdw_debug_info *ptr)
-{
- ptr->big_lock_held = hdw->big_lock_held;
- ptr->ctl_lock_held = hdw->ctl_lock_held;
- ptr->flag_disconnected = hdw->flag_disconnected;
- ptr->flag_init_ok = hdw->flag_init_ok;
- ptr->flag_ok = hdw->flag_ok;
- ptr->fw1_state = hdw->fw1_state;
- ptr->flag_decoder_missed = hdw->flag_decoder_missed;
- ptr->flag_tripped = hdw->flag_tripped;
- ptr->state_encoder_ok = hdw->state_encoder_ok;
- ptr->state_encoder_run = hdw->state_encoder_run;
- ptr->state_decoder_run = hdw->state_decoder_run;
- ptr->state_usbstream_run = hdw->state_usbstream_run;
- ptr->state_decoder_quiescent = hdw->state_decoder_quiescent;
- ptr->state_pipeline_config = hdw->state_pipeline_config;
- ptr->state_pipeline_req = hdw->state_pipeline_req;
- ptr->state_pipeline_pause = hdw->state_pipeline_pause;
- ptr->state_pipeline_idle = hdw->state_pipeline_idle;
- ptr->cmd_debug_state = hdw->cmd_debug_state;
- ptr->cmd_code = hdw->cmd_debug_code;
- ptr->cmd_debug_write_len = hdw->cmd_debug_write_len;
- ptr->cmd_debug_read_len = hdw->cmd_debug_read_len;
- ptr->cmd_debug_timeout = hdw->ctl_timeout_flag;
- ptr->cmd_debug_write_pend = hdw->ctl_write_pend_flag;
- ptr->cmd_debug_read_pend = hdw->ctl_read_pend_flag;
- ptr->cmd_debug_rstatus = hdw->ctl_read_urb->status;
- ptr->cmd_debug_wstatus = hdw->ctl_read_urb->status;
-}
-
-
-void pvr2_hdw_get_debug_info_locked(struct pvr2_hdw *hdw,
- struct pvr2_hdw_debug_info *ptr)
-{
- LOCK_TAKE(hdw->ctl_lock); do {
- pvr2_hdw_get_debug_info_unlocked(hdw,ptr);
- } while(0); LOCK_GIVE(hdw->ctl_lock);
-}
-
-
int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
{
return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
@@ -3756,6 +4259,80 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
}
+unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
+{
+ return hdw->input_avail_mask;
+}
+
+
+unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *hdw)
+{
+ return hdw->input_allowed_mask;
+}
+
+
+static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v)
+{
+ if (hdw->input_val != v) {
+ hdw->input_val = v;
+ hdw->input_dirty = !0;
+ }
+
+ /* Handle side effects - if we switch to a mode that needs the RF
+ tuner, then select the right frequency choice as well and mark
+ it dirty. */
+ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+ hdw->freqSelector = 0;
+ hdw->freqDirty = !0;
+ } else if ((hdw->input_val == PVR2_CVAL_INPUT_TV) ||
+ (hdw->input_val == PVR2_CVAL_INPUT_DTV)) {
+ hdw->freqSelector = 1;
+ hdw->freqDirty = !0;
+ }
+ return 0;
+}
+
+
+int pvr2_hdw_set_input_allowed(struct pvr2_hdw *hdw,
+ unsigned int change_mask,
+ unsigned int change_val)
+{
+ int ret = 0;
+ unsigned int nv,m,idx;
+ LOCK_TAKE(hdw->big_lock);
+ do {
+ nv = hdw->input_allowed_mask & ~change_mask;
+ nv |= (change_val & change_mask);
+ nv &= hdw->input_avail_mask;
+ if (!nv) {
+ /* No legal modes left; return error instead. */
+ ret = -EPERM;
+ break;
+ }
+ hdw->input_allowed_mask = nv;
+ if ((1 << hdw->input_val) & hdw->input_allowed_mask) {
+ /* Current mode is still in the allowed mask, so
+ we're done. */
+ break;
+ }
+ /* Select and switch to a mode that is still in the allowed
+ mask */
+ if (!hdw->input_allowed_mask) {
+ /* Nothing legal; give up */
+ break;
+ }
+ m = hdw->input_allowed_mask;
+ for (idx = 0; idx < (sizeof(m) << 3); idx++) {
+ if (!((1 << idx) & m)) continue;
+ pvr2_hdw_set_input(hdw,idx);
+ break;
+ }
+ } while (0);
+ LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
/* Find I2C address of eeprom */
static int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
{
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
index 3ad7a13d6c3..20295e0c199 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -40,9 +40,10 @@
/* Legal values for the INPUT state variable */
#define PVR2_CVAL_INPUT_TV 0
-#define PVR2_CVAL_INPUT_SVIDEO 1
+#define PVR2_CVAL_INPUT_DTV 1
#define PVR2_CVAL_INPUT_COMPOSITE 2
-#define PVR2_CVAL_INPUT_RADIO 3
+#define PVR2_CVAL_INPUT_SVIDEO 3
+#define PVR2_CVAL_INPUT_RADIO 4
enum pvr2_config {
pvr2_config_empty, /* No configuration */
@@ -90,9 +91,6 @@ enum pvr2_v4l_type {
/* Translate configuration enum to a string label */
const char *pvr2_config_get_name(enum pvr2_config);
-/* Translate a master state enum to a string label */
-const char *pvr2_hdw_get_state_name(unsigned int);
-
struct pvr2_hdw;
/* Create and return a structure for interacting with the underlying
@@ -100,14 +98,15 @@ struct pvr2_hdw;
struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
const struct usb_device_id *devid);
+/* Perform second stage initialization, passing in a notification callback
+ for when the master state changes. */
+int pvr2_hdw_initialize(struct pvr2_hdw *,
+ void (*callback_func)(void *),
+ void *callback_data);
+
/* Destroy hardware interaction structure */
void pvr2_hdw_destroy(struct pvr2_hdw *);
-/* Register a function to be called whenever the master state changes. */
-void pvr2_hdw_set_state_callback(struct pvr2_hdw *,
- void (*callback_func)(void *),
- void *callback_data);
-
/* Return true if in the ready (normal) state */
int pvr2_hdw_dev_ok(struct pvr2_hdw *);
@@ -146,6 +145,23 @@ struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *,
/* Commit all control changes made up to this point */
int pvr2_hdw_commit_ctl(struct pvr2_hdw *);
+/* Return a bit mask of valid input selections for this device. Mask bits
+ * will be according to PVR_CVAL_INPUT_xxxx definitions. */
+unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *);
+
+/* Return a bit mask of allowed input selections for this device. Mask bits
+ * will be according to PVR_CVAL_INPUT_xxxx definitions. */
+unsigned int pvr2_hdw_get_input_allowed(struct pvr2_hdw *);
+
+/* Change the set of allowed input selections for this device. Both
+ change_mask and change_valu are mask bits according to
+ PVR_CVAL_INPUT_xxxx definitions. The change_mask parameter indicate
+ which settings are being changed and the change_val parameter indicates
+ whether corresponding settings are being set or cleared. */
+int pvr2_hdw_set_input_allowed(struct pvr2_hdw *,
+ unsigned int change_mask,
+ unsigned int change_val);
+
/* Return name for this driver instance */
const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *);
@@ -250,6 +266,9 @@ int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *);
/* Execute simple reset command */
int pvr2_hdw_cmd_powerup(struct pvr2_hdw *);
+/* suspend */
+int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *);
+
/* Order decoder to reset */
int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index 62867fa3517..793c89a8d67 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -35,7 +35,7 @@
*/
-static unsigned int i2c_scan = 0;
+static unsigned int i2c_scan;
module_param(i2c_scan, int, S_IRUGO|S_IWUSR);
MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c
index a9889ff96ec..7aff8b72006 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-io.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.c
@@ -80,6 +80,10 @@ struct pvr2_stream {
/* Tracking state for tolerating errors */
unsigned int fail_count;
unsigned int fail_tolerance;
+
+ unsigned int buffers_processed;
+ unsigned int buffers_failed;
+ unsigned int bytes_processed;
};
struct pvr2_buffer {
@@ -446,6 +450,8 @@ static void buffer_complete(struct urb *urb)
(urb->status == -ENOENT) ||
(urb->status == -ECONNRESET) ||
(urb->status == -ESHUTDOWN)) {
+ (sp->buffers_processed)++;
+ sp->bytes_processed += urb->actual_length;
bp->used_count = urb->actual_length;
if (sp->fail_count) {
pvr2_trace(PVR2_TRACE_TOLERANCE,
@@ -457,11 +463,13 @@ static void buffer_complete(struct urb *urb)
// We can tolerate this error, because we're below the
// threshold...
(sp->fail_count)++;
+ (sp->buffers_failed)++;
pvr2_trace(PVR2_TRACE_TOLERANCE,
"stream %p ignoring error %d"
" - fail count increased to %u",
sp,urb->status,sp->fail_count);
} else {
+ (sp->buffers_failed)++;
bp->status = urb->status;
}
spin_unlock_irqrestore(&sp->list_lock,irq_flags);
@@ -515,6 +523,28 @@ void pvr2_stream_set_callback(struct pvr2_stream *sp,
} while(0); mutex_unlock(&sp->mutex);
}
+void pvr2_stream_get_stats(struct pvr2_stream *sp,
+ struct pvr2_stream_stats *stats,
+ int zero_counts)
+{
+ unsigned long irq_flags;
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ if (stats) {
+ stats->buffers_in_queue = sp->q_count;
+ stats->buffers_in_idle = sp->i_count;
+ stats->buffers_in_ready = sp->r_count;
+ stats->buffers_processed = sp->buffers_processed;
+ stats->buffers_failed = sp->buffers_failed;
+ stats->bytes_processed = sp->bytes_processed;
+ }
+ if (zero_counts) {
+ sp->buffers_processed = 0;
+ sp->buffers_failed = 0;
+ sp->bytes_processed = 0;
+ }
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
/* Query / set the nominal buffer count */
int pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
{
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h
index 93279cc2a35..42fcf8281a8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-io.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.h
@@ -36,6 +36,15 @@ enum pvr2_buffer_state {
struct pvr2_stream;
struct pvr2_buffer;
+struct pvr2_stream_stats {
+ unsigned int buffers_in_queue;
+ unsigned int buffers_in_idle;
+ unsigned int buffers_in_ready;
+ unsigned int buffers_processed;
+ unsigned int buffers_failed;
+ unsigned int bytes_processed;
+};
+
/* Initialize / tear down stream structure */
struct pvr2_stream *pvr2_stream_create(void);
void pvr2_stream_destroy(struct pvr2_stream *);
@@ -45,6 +54,9 @@ void pvr2_stream_setup(struct pvr2_stream *,
void pvr2_stream_set_callback(struct pvr2_stream *,
pvr2_stream_callback func,
void *data);
+void pvr2_stream_get_stats(struct pvr2_stream *,
+ struct pvr2_stream_stats *,
+ int zero_counts);
/* Query / set the nominal buffer count */
int pvr2_stream_get_buffer_count(struct pvr2_stream *);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
index b63b2265503..332aced8a5a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -60,6 +60,10 @@ static void pvr_setup_attach(struct pvr2_context *pvr)
{
/* Create association with v4l layer */
pvr2_v4l2_create(pvr);
+#ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ /* Create association with dvb layer */
+ pvr2_dvb_create(pvr);
+#endif
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
pvr2_sysfs_create(pvr,class_ptr);
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
@@ -121,6 +125,12 @@ static int __init pvr_init(void)
pvr2_trace(PVR2_TRACE_INIT,"pvr_init");
+ ret = pvr2_context_global_init();
+ if (ret != 0) {
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init failure code=%d",ret);
+ return ret;
+ }
+
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
class_ptr = pvr2_sysfs_class_create();
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
@@ -132,6 +142,8 @@ static int __init pvr_init(void)
if (pvrusb2_debug) info("Debug mask is %d (0x%x)",
pvrusb2_debug,pvrusb2_debug);
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete");
+
return ret;
}
@@ -144,6 +156,10 @@ static void __exit pvr_exit(void)
#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
pvr2_sysfs_class_destroy(class_ptr);
#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+ pvr2_context_global_done();
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_exit complete");
}
module_init(pvr_init);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c
index da309288daa..fdc5a2b49ca 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-std.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.c
@@ -79,7 +79,7 @@ struct std_name {
#define TSTD_Nc (V4L2_STD_PAL_Nc)
#define TSTD_60 (V4L2_STD_PAL_60)
-#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM)
+#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_ATSC|CSTD_SECAM)
/* Mapping of standard bits to color system */
static const struct std_name std_groups[] = {
@@ -328,7 +328,7 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
struct v4l2_standard *stddefs;
if (pvrusb2_debug & PVR2_TRACE_STD) {
- char buf[50];
+ char buf[100];
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id);
pvr2_trace(
PVR2_TRACE_STD,"Mapping standards mask=0x%x (%.*s)",
@@ -352,8 +352,11 @@ struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++;
}
+ /* Don't complain about ATSC standard values */
+ fmsk &= ~CSTD_ATSC;
+
if (fmsk) {
- char buf[50];
+ char buf[100];
bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk);
pvr2_trace(
PVR2_TRACE_ERROR_LEGS,
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index 07f4eae1843..0ff7a836a8a 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -287,6 +287,8 @@ static ssize_t store_val_norm(int id,struct device *class_dev,
struct pvr2_sysfs *sfp;
int ret;
sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_norm(cid=%d) \"%.*s\"",
+ sfp,id,(int)count,buf);
ret = store_val_any(id,0,sfp,buf,count);
if (!ret) ret = count;
return ret;
@@ -298,6 +300,8 @@ static ssize_t store_val_custom(int id,struct device *class_dev,
struct pvr2_sysfs *sfp;
int ret;
sfp = (struct pvr2_sysfs *)class_dev->driver_data;
+ pvr2_sysfs_trace("pvr2_sysfs(%p) store_val_custom(cid=%d) \"%.*s\"",
+ sfp,id,(int)count,buf);
ret = store_val_any(id,1,sfp,buf,count);
if (!ret) ret = count;
return ret;
@@ -604,8 +608,9 @@ static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
ret = sysfs_create_group(&sfp->class_dev->kobj,&cip->grp);
if (ret) {
- printk(KERN_WARNING "%s: sysfs_create_group error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "sysfs_create_group error: %d",
+ ret);
return;
}
cip->created_ok = !0;
@@ -636,15 +641,17 @@ static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp)
sfp->debugifc = dip;
ret = device_create_file(sfp->class_dev,&dip->attr_debugcmd);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
dip->debugcmd_created_ok = !0;
}
ret = device_create_file(sfp->class_dev,&dip->attr_debuginfo);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
dip->debuginfo_created_ok = !0;
}
@@ -847,8 +854,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
class_dev->driver_data = sfp;
ret = device_register(class_dev);
if (ret) {
- printk(KERN_ERR "%s: device_register failed\n",
- __FUNCTION__);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_register failed");
kfree(class_dev);
return;
}
@@ -860,8 +867,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
ret = device_create_file(sfp->class_dev,
&sfp->attr_v4l_minor_number);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
sfp->v4l_minor_number_created_ok = !0;
}
@@ -873,8 +881,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
ret = device_create_file(sfp->class_dev,
&sfp->attr_v4l_radio_minor_number);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
sfp->v4l_radio_minor_number_created_ok = !0;
}
@@ -885,8 +894,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
sfp->attr_unit_number.store = NULL;
ret = device_create_file(sfp->class_dev,&sfp->attr_unit_number);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
sfp->unit_number_created_ok = !0;
}
@@ -898,8 +908,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
ret = device_create_file(sfp->class_dev,
&sfp->attr_bus_info);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
sfp->bus_info_created_ok = !0;
}
@@ -911,8 +922,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
ret = device_create_file(sfp->class_dev,
&sfp->attr_hdw_name);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
sfp->hdw_name_created_ok = !0;
}
@@ -924,8 +936,9 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
ret = device_create_file(sfp->class_dev,
&sfp->attr_hdw_desc);
if (ret < 0) {
- printk(KERN_WARNING "%s: device_create_file error: %d\n",
- __FUNCTION__, ret);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "device_create_file error: %d",
+ ret);
} else {
sfp->hdw_desc_created_ok = !0;
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 8f0587ebd4b..087a1824556 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -57,7 +57,9 @@ struct pvr2_v4l2_fh {
struct pvr2_v4l2_fh *vprev;
wait_queue_head_t wait_data;
int fw_mode_flag;
- int prev_input_val;
+ /* Map contiguous ordinal value to input id */
+ unsigned char *input_map;
+ unsigned int input_cnt;
};
struct pvr2_v4l2 {
@@ -259,14 +261,21 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
struct v4l2_input *vi = (struct v4l2_input *)arg;
struct v4l2_input tmp;
unsigned int cnt;
+ int val;
cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
memset(&tmp,0,sizeof(tmp));
tmp.index = vi->index;
ret = 0;
- switch (vi->index) {
+ if ((vi->index < 0) || (vi->index >= fh->input_cnt)) {
+ ret = -EINVAL;
+ break;
+ }
+ val = fh->input_map[vi->index];
+ switch (val) {
case PVR2_CVAL_INPUT_TV:
+ case PVR2_CVAL_INPUT_DTV:
case PVR2_CVAL_INPUT_RADIO:
tmp.type = V4L2_INPUT_TYPE_TUNER;
break;
@@ -281,7 +290,7 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
if (ret < 0) break;
cnt = 0;
- pvr2_ctrl_get_valname(cptr,vi->index,
+ pvr2_ctrl_get_valname(cptr,val,
tmp.name,sizeof(tmp.name)-1,&cnt);
tmp.name[cnt] = 0;
@@ -303,22 +312,33 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_G_INPUT:
{
+ unsigned int idx;
struct pvr2_ctrl *cptr;
struct v4l2_input *vi = (struct v4l2_input *)arg;
int val;
cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
val = 0;
ret = pvr2_ctrl_get_value(cptr,&val);
- vi->index = val;
+ vi->index = 0;
+ for (idx = 0; idx < fh->input_cnt; idx++) {
+ if (fh->input_map[idx] == val) {
+ vi->index = idx;
+ break;
+ }
+ }
break;
}
case VIDIOC_S_INPUT:
{
struct v4l2_input *vi = (struct v4l2_input *)arg;
+ if ((vi->index < 0) || (vi->index >= fh->input_cnt)) {
+ ret = -ERANGE;
+ break;
+ }
ret = pvr2_ctrl_set_value(
pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
- vi->index);
+ fh->input_map[vi->index]);
break;
}
@@ -858,7 +878,6 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file)
{
struct pvr2_v4l2_fh *fhp = file->private_data;
struct pvr2_v4l2 *vp = fhp->vhead;
- struct pvr2_context *mp = fhp->vhead->channel.mc_head;
struct pvr2_hdw *hdw = fhp->channel.mc_head->hdw;
pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
@@ -875,42 +894,30 @@ static int pvr2_v4l2_release(struct inode *inode, struct file *file)
v4l2_prio_close(&vp->prio, &fhp->prio);
file->private_data = NULL;
- pvr2_context_enter(mp); do {
- /* Restore the previous input selection, if it makes sense
- to do so. */
- if (fhp->dev_info->v4l_type == VFL_TYPE_RADIO) {
- struct pvr2_ctrl *cp;
- int pval;
- cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
- pvr2_ctrl_get_value(cp,&pval);
- /* Only restore if we're still selecting the radio */
- if (pval == PVR2_CVAL_INPUT_RADIO) {
- pvr2_ctrl_set_value(cp,fhp->prev_input_val);
- pvr2_hdw_commit_ctl(hdw);
- }
- }
-
- if (fhp->vnext) {
- fhp->vnext->vprev = fhp->vprev;
- } else {
- vp->vlast = fhp->vprev;
- }
- if (fhp->vprev) {
- fhp->vprev->vnext = fhp->vnext;
- } else {
- vp->vfirst = fhp->vnext;
- }
- fhp->vnext = NULL;
- fhp->vprev = NULL;
- fhp->vhead = NULL;
- pvr2_channel_done(&fhp->channel);
- pvr2_trace(PVR2_TRACE_STRUCT,
- "Destroying pvr_v4l2_fh id=%p",fhp);
- kfree(fhp);
- if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
- pvr2_v4l2_destroy_no_lock(vp);
- }
- } while (0); pvr2_context_exit(mp);
+ if (fhp->vnext) {
+ fhp->vnext->vprev = fhp->vprev;
+ } else {
+ vp->vlast = fhp->vprev;
+ }
+ if (fhp->vprev) {
+ fhp->vprev->vnext = fhp->vnext;
+ } else {
+ vp->vfirst = fhp->vnext;
+ }
+ fhp->vnext = NULL;
+ fhp->vprev = NULL;
+ fhp->vhead = NULL;
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p",fhp);
+ if (fhp->input_map) {
+ kfree(fhp->input_map);
+ fhp->input_map = NULL;
+ }
+ kfree(fhp);
+ if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
+ pvr2_v4l2_destroy_no_lock(vp);
+ }
return 0;
}
@@ -921,6 +928,9 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file)
struct pvr2_v4l2_fh *fhp;
struct pvr2_v4l2 *vp;
struct pvr2_hdw *hdw;
+ unsigned int input_mask = 0;
+ unsigned int input_cnt,idx;
+ int ret = 0;
dip = container_of(video_devdata(file),struct pvr2_v4l2_dev,devbase);
@@ -943,32 +953,62 @@ static int pvr2_v4l2_open(struct inode *inode, struct file *file)
init_waitqueue_head(&fhp->wait_data);
fhp->dev_info = dip;
- pvr2_context_enter(vp->channel.mc_head); do {
- pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
- pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
+ pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
- fhp->vnext = NULL;
- fhp->vprev = vp->vlast;
- if (vp->vlast) {
- vp->vlast->vnext = fhp;
- } else {
- vp->vfirst = fhp;
- }
- vp->vlast = fhp;
- fhp->vhead = vp;
-
- /* Opening the /dev/radioX device implies a mode switch.
- So execute that here. Note that you can get the
- IDENTICAL effect merely by opening the normal video
- device and setting the input appropriately. */
- if (dip->v4l_type == VFL_TYPE_RADIO) {
- struct pvr2_ctrl *cp;
- cp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
- pvr2_ctrl_get_value(cp,&fhp->prev_input_val);
- pvr2_ctrl_set_value(cp,PVR2_CVAL_INPUT_RADIO);
- pvr2_hdw_commit_ctl(hdw);
- }
- } while (0); pvr2_context_exit(vp->channel.mc_head);
+ if (dip->v4l_type == VFL_TYPE_RADIO) {
+ /* Opening device as a radio, legal input selection subset
+ is just the radio. */
+ input_mask = (1 << PVR2_CVAL_INPUT_RADIO);
+ } else {
+ /* Opening the main V4L device, legal input selection
+ subset includes all analog inputs. */
+ input_mask = ((1 << PVR2_CVAL_INPUT_RADIO) |
+ (1 << PVR2_CVAL_INPUT_TV) |
+ (1 << PVR2_CVAL_INPUT_COMPOSITE) |
+ (1 << PVR2_CVAL_INPUT_SVIDEO));
+ }
+ ret = pvr2_channel_limit_inputs(&fhp->channel,input_mask);
+ if (ret) {
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p (input mask error)",
+ fhp);
+
+ kfree(fhp);
+ return ret;
+ }
+
+ input_mask &= pvr2_hdw_get_input_available(hdw);
+ input_cnt = 0;
+ for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+ if (input_mask & (1 << idx)) input_cnt++;
+ }
+ fhp->input_cnt = input_cnt;
+ fhp->input_map = kzalloc(input_cnt,GFP_KERNEL);
+ if (!fhp->input_map) {
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p (input map failure)",
+ fhp);
+ kfree(fhp);
+ return -ENOMEM;
+ }
+ input_cnt = 0;
+ for (idx = 0; idx < (sizeof(input_mask) << 3); idx++) {
+ if (!(input_mask & (1 << idx))) continue;
+ fhp->input_map[input_cnt++] = idx;
+ }
+
+ fhp->vnext = NULL;
+ fhp->vprev = vp->vlast;
+ if (vp->vlast) {
+ vp->vlast->vnext = fhp;
+ } else {
+ vp->vfirst = fhp;
+ }
+ vp->vlast = fhp;
+ fhp->vhead = vp;
fhp->file = file;
file->private_data = fhp;
@@ -1201,24 +1241,27 @@ struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
vp = kzalloc(sizeof(*vp),GFP_KERNEL);
if (!vp) return vp;
- vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
- vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
- if (!(vp->dev_video && vp->dev_radio)) {
- kfree(vp->dev_video);
- kfree(vp->dev_radio);
- kfree(vp);
- return NULL;
- }
pvr2_channel_init(&vp->channel,mnp);
pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
vp->channel.check_func = pvr2_v4l2_internal_check;
/* register streams */
+ vp->dev_video = kzalloc(sizeof(*vp->dev_video),GFP_KERNEL);
+ if (!vp->dev_video) goto fail;
pvr2_v4l2_dev_init(vp->dev_video,vp,VFL_TYPE_GRABBER);
- pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
+ if (pvr2_hdw_get_input_available(vp->channel.mc_head->hdw) &
+ (1 << PVR2_CVAL_INPUT_RADIO)) {
+ vp->dev_radio = kzalloc(sizeof(*vp->dev_radio),GFP_KERNEL);
+ if (!vp->dev_radio) goto fail;
+ pvr2_v4l2_dev_init(vp->dev_radio,vp,VFL_TYPE_RADIO);
+ }
return vp;
+ fail:
+ pvr2_trace(PVR2_TRACE_STRUCT,"Failure creating pvr2_v4l2 id=%p",vp);
+ pvr2_v4l2_destroy_no_lock(vp);
+ return 0;
}
/*